/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Netscape Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org 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): * Pierre Phaneuf * Peter Annema * * * Alternatively, the contents of this file may be used under the terms of * either 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 NPL, 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 NPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsCOMPtr.h" #include "nsXPIDLString.h" #include "nsIHTMLContentSink.h" #include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestorUtils.h" #include "nsIParser.h" #include "nsParserUtils.h" #include "nsICSSStyleSheet.h" #include "nsICSSLoader.h" #include "nsICSSLoaderObserver.h" #include "nsIScriptLoader.h" #include "nsIScriptLoaderObserver.h" #include "nsIHTMLContent.h" #include "nsIHTMLContentContainer.h" #include "nsIUnicharInputStream.h" #include "nsIURL.h" #include "nsNetUtil.h" #include "nsIPresShell.h" #include "nsIPresContext.h" #include "nsIViewManager.h" #include "nsIContentViewer.h" #include "nsIMarkupDocumentViewer.h" #include "nsINodeInfo.h" #include "nsHTMLTokens.h" #include "nsCRT.h" #include "nsSupportsArray.h" #include "jsapi.h" // for JSVERSION_* and JS_VersionToString #include "prtime.h" #include "prlog.h" #include "nsGenericHTMLElement.h" #include "nsIElementFactory.h" #include "nsITextContent.h" #include "nsIDOMText.h" #include "nsIDOMComment.h" #include "nsIDOMHTMLDocument.h" #include "nsIDOMDOMImplementation.h" #include "nsIDOMDocumentType.h" #include "nsIDOMHTMLScriptElement.h" #include "nsIScriptElement.h" #include "nsIDOMHTMLFormElement.h" #include "nsIDOMHTMLTextAreaElement.h" #include "nsIDOMHTMLOptionElement.h" #include "nsIFormControl.h" #include "nsIComponentManager.h" #include "nsIServiceManager.h" #include "nsIScrollableView.h" #include "nsHTMLAtoms.h" #include "nsContentUtils.h" #include "nsIFrame.h" #include "nsICharsetConverterManager.h" #include "nsICharsetConverterManager2.h" #include "nsIUnicodeDecoder.h" #include "nsICharsetAlias.h" #include "nsIChannel.h" #include "nsIHttpChannel.h" #include "nsIWebShell.h" #include "nsIDocShell.h" #include "nsIWebNavigation.h" #include "nsIDocument.h" #include "nsIDocumentObserver.h" #include "nsIHTMLDocument.h" #include "nsStyleConsts.h" #include "nsINameSpaceManager.h" #include "nsIDOMHTMLMapElement.h" #include "nsIRefreshURI.h" #include "nsICookieService.h" #include "nsVoidArray.h" #include "nsIScriptSecurityManager.h" #include "nsIPrincipal.h" #include "nsHTMLIIDs.h" #include "nsTextFragment.h" #include "nsIScriptGlobalObject.h" #include "nsIScriptGlobalObjectOwner.h" #include "nsIParserService.h" #include "nsParserCIID.h" #include "nsISelectElement.h" #include "nsIPref.h" // XXX Go through a factory for this one #include "nsICSSParser.h" #include "nsIStyleSheetLinkingElement.h" #include "nsIDOMHTMLTitleElement.h" #include "nsTimer.h" #include "nsITimer.h" #include "nsITimerCallback.h" #include "nsDOMError.h" #include "nsIScrollable.h" #include "nsContentPolicyUtils.h" #include "nsIScriptContext.h" #include "nsStyleLinkElement.h" #include "nsReadableUtils.h" #include "nsWeakReference.h"//nshtmlelementfactory supports weak references #include "nsIPrompt.h" #include "nsIDOMWindowInternal.h" #include "nsLayoutCID.h" #include "nsIFrameManager.h" #include "nsILayoutHistoryState.h" #include "nsEscape.h" static NS_DEFINE_CID(kLayoutHistoryStateCID, NS_LAYOUT_HISTORY_STATE_CID); #ifdef ALLOW_ASYNCH_STYLE_SHEETS const PRBool kBlockByDefault=PR_FALSE; #else const PRBool kBlockByDefault=PR_TRUE; #endif static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID); static NS_DEFINE_IID(kParserServiceCID, NS_PARSERSERVICE_CID); //---------------------------------------------------------------------- #ifdef NS_DEBUG static PRLogModuleInfo* gSinkLogModuleInfo; #define SINK_TRACE_CALLS 0x1 #define SINK_TRACE_REFLOW 0x2 #define SINK_ALWAYS_REFLOW 0x4 #define SINK_LOG_TEST(_lm,_bit) (PRIntn((_lm)->level) & (_bit)) #define SINK_TRACE(_bit,_args) \ PR_BEGIN_MACRO \ if (SINK_LOG_TEST(gSinkLogModuleInfo,_bit)) { \ PR_LogPrint _args; \ } \ PR_END_MACRO #define SINK_TRACE_NODE(_bit,_msg,_node,_sp,_obj) _obj->SinkTraceNode(_bit,_msg,_node,_sp,this) #else #define SINK_TRACE(_bit,_args) #define SINK_TRACE_NODE(_bit,_msg,_node,_sp,_obj) #endif #undef SINK_NO_INCREMENTAL //---------------------------------------------------------------------- #define NS_SINK_FLAG_SCRIPT_ENABLED 0x8 #define NS_SINK_FLAG_FRAMES_ENABLED 0x10 #define NS_SINK_FLAG_CAN_INTERRUPT_PARSER 0x20 //Interrupt parsing when mMaxTokenProcessingTime is exceeded #define NS_SINK_FLAG_DYNAMIC_LOWER_VALUE 0x40 // Lower the value for mNotificationInterval and mMaxTokenProcessingTime #define NS_DELAY_FOR_WINDOW_CREATION 500000 // 1/2 second fudge factor for window creation class SinkContext; class HTMLContentSink : public nsIHTMLContentSink, public nsIScriptLoaderObserver, public nsITimerCallback, public nsICSSLoaderObserver, public nsIDocumentObserver #ifdef DEBUG ,public nsIDebugDumpContent #endif { public: HTMLContentSink(); virtual ~HTMLContentSink(); NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW nsresult Init(nsIDocument* aDoc, nsIURI* aURL, nsIWebShell* aContainer, nsIChannel* aChannel); // nsISupports NS_DECL_ISUPPORTS NS_DECL_NSISCRIPTLOADEROBSERVER // nsIContentSink NS_IMETHOD WillBuildModel(void); NS_IMETHOD DidBuildModel(PRInt32 aQualityLevel); NS_IMETHOD WillInterrupt(void); NS_IMETHOD WillResume(void); NS_IMETHOD SetParser(nsIParser* aParser); NS_IMETHOD OpenContainer(const nsIParserNode& aNode); NS_IMETHOD CloseContainer(const nsIParserNode& aNode); NS_IMETHOD AddLeaf(const nsIParserNode& aNode); NS_IMETHOD NotifyError(const nsParserError* aError); NS_IMETHOD FlushPendingNotifications(); NS_IMETHOD SetDocumentCharset(nsAWritableString& aCharset); NS_IMETHOD AddComment(const nsIParserNode& aNode); NS_IMETHOD AddProcessingInstruction(const nsIParserNode& aNode); NS_IMETHOD AddDocTypeDecl(const nsIParserNode& aNode, PRInt32 aMode=0); NS_IMETHOD WillProcessTokens(void); NS_IMETHOD DidProcessTokens(void); NS_IMETHOD WillProcessAToken(void); NS_IMETHOD DidProcessAToken(void); NS_IMETHOD NotifyTagObservers(nsIParserNode* aNode); // nsIHTMLContentSink NS_IMETHOD BeginContext(PRInt32 aID); NS_IMETHOD EndContext(PRInt32 aID); NS_IMETHOD SetTitle(const nsString& aValue); NS_IMETHOD OpenHTML(const nsIParserNode& aNode); NS_IMETHOD CloseHTML(const nsIParserNode& aNode); NS_IMETHOD OpenHead(const nsIParserNode& aNode); NS_IMETHOD CloseHead(const nsIParserNode& aNode); NS_IMETHOD OpenBody(const nsIParserNode& aNode); NS_IMETHOD CloseBody(const nsIParserNode& aNode); NS_IMETHOD OpenForm(const nsIParserNode& aNode); NS_IMETHOD CloseForm(const nsIParserNode& aNode); NS_IMETHOD OpenFrameset(const nsIParserNode& aNode); NS_IMETHOD CloseFrameset(const nsIParserNode& aNode); NS_IMETHOD OpenMap(const nsIParserNode& aNode); NS_IMETHOD CloseMap(const nsIParserNode& aNode); NS_IMETHOD GetPref(PRInt32 aTag,PRBool& aPref); NS_IMETHOD DoFragment(PRBool aFlag); // nsITimerCallback NS_IMETHOD_(void) Notify(nsITimer *timer); // nsICSSLoaderObserver NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet* aSheet, PRBool aNotify) { return NS_OK; }; // nsIDocumentObserver NS_IMETHOD BeginUpdate(nsIDocument *aDocument); NS_IMETHOD EndUpdate(nsIDocument *aDocument); NS_IMETHOD BeginLoad(nsIDocument *aDocument) { return NS_OK; } NS_IMETHOD EndLoad(nsIDocument *aDocument) { return NS_OK; } NS_IMETHOD BeginReflow(nsIDocument *aDocument, nsIPresShell* aShell) { return NS_OK; } NS_IMETHOD EndReflow(nsIDocument *aDocument, nsIPresShell* aShell) { return NS_OK; } NS_IMETHOD ContentChanged(nsIDocument *aDocument, nsIContent* aContent, nsISupports* aSubContent) { return NS_OK; } NS_IMETHOD ContentStatesChanged(nsIDocument* aDocument, nsIContent* aContent1, nsIContent* aContent2) { return NS_OK; } NS_IMETHOD AttributeChanged(nsIDocument *aDocument, nsIContent* aContent, PRInt32 aNameSpaceID, nsIAtom* aAttribute, PRInt32 aModType, PRInt32 aHint) { return NS_OK; } NS_IMETHOD ContentAppended(nsIDocument *aDocument, nsIContent* aContainer, PRInt32 aNewIndexInContainer) { return NS_OK; } NS_IMETHOD ContentInserted(nsIDocument *aDocument, nsIContent* aContainer, nsIContent* aChild, PRInt32 aIndexInContainer) { return NS_OK; } NS_IMETHOD ContentReplaced(nsIDocument *aDocument, nsIContent* aContainer, nsIContent* aOldChild, nsIContent* aNewChild, PRInt32 aIndexInContainer) { return NS_OK; } NS_IMETHOD ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer, nsIContent* aChild, PRInt32 aIndexInContainer) { return NS_OK; } NS_IMETHOD StyleSheetAdded(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet); NS_IMETHOD StyleSheetRemoved(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet); NS_IMETHOD StyleSheetDisabledStateChanged(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet, PRBool aDisabled); NS_IMETHOD StyleRuleChanged(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet, nsIStyleRule* aStyleRule, PRInt32 aHint) { return NS_OK; } NS_IMETHOD StyleRuleAdded(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet, nsIStyleRule* aStyleRule) { return NS_OK; } NS_IMETHOD StyleRuleRemoved(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet, nsIStyleRule* aStyleRule) { return NS_OK; } NS_IMETHOD DocumentWillBeDestroyed(nsIDocument *aDocument) { return NS_OK; } #ifdef DEBUG // nsIDebugDumpContent NS_IMETHOD DumpContentModel(); #endif PRBool IsTimeToNotify(); PRBool IsInScript(); void ReduceEntities(nsString& aString); void GetAttributeValueAt(const nsIParserNode& aNode, PRInt32 aIndex, nsString& aResult); nsresult AddAttributes(const nsIParserNode& aNode, nsIHTMLContent* aContent, PRBool aNotify = PR_FALSE); nsresult CreateContentObject(const nsIParserNode& aNode, nsHTMLTag aNodeType, nsIDOMHTMLFormElement* aForm, nsIWebShell* aWebShell, nsIHTMLContent** aResult); inline PRInt32 GetNotificationInterval() { return ((mFlags & NS_SINK_FLAG_DYNAMIC_LOWER_VALUE) ? 1000 : mNotificationInterval); }; inline PRInt32 GetMaxTokenProcessingTime() { return ((mFlags & NS_SINK_FLAG_DYNAMIC_LOWER_VALUE) ? 3000 : mMaxTokenProcessingTime); }; #ifdef NS_DEBUG void SinkTraceNode(PRUint32 aBit, const char* aMsg, const nsIParserNode& aNode, PRInt32 aStackPos, void* aThis); #endif nsIDocument* mDocument; nsIHTMLDocument* mHTMLDocument; nsCOMPtr mNodeInfoManager; nsIURI* mDocumentURI; nsIURI* mDocumentBaseURL; nsIWebShell* mWebShell; nsIParser* mParser; PRInt32 mBackoffCount; // back off timer notification after count PRInt32 mNotificationInterval; // Notification interval in microseconds PRTime mLastNotificationTime; // Time of last notification nsCOMPtr mNotificationTimer; // Timer used for notification PRInt32 mMaxTextRun; // The maximum length of a text run nsIHTMLContent* mRoot; nsIHTMLContent* mBody; nsIHTMLContent* mFrameset; nsIHTMLContent* mHead; nsString* mTitle; nsString mUnicodeXferBuf; PRPackedBool mNotifyOnTimer; // Do we notify based on time? PRPackedBool mLayoutStarted; PRPackedBool mIsDemotingContainer; PRPackedBool mScrolledToRefAlready; PRPackedBool mNeedToBlockParser; PRInt32 mInScript; PRInt32 mInNotification; nsIDOMHTMLFormElement* mCurrentForm; nsIHTMLContent* mCurrentMap; nsAutoVoidArray mContextStack; SinkContext* mCurrentContext; SinkContext* mHeadContext; PRInt32 mNumOpenIFRAMES; nsSupportsArray mScriptElements; nsCOMPtr mDummyParserRequest; nsCString mRef; nsString mBaseHREF; nsString mBaseTarget; PRInt32 mStyleSheetCount; nsICSSLoader* mCSSLoader; PRInt32 mInsideNoXXXTag; PRInt32 mInMonolithicContainer; PRUint32 mFlags; // Can interrupt parsing members PRUint32 mDelayTimerStart; PRInt32 mMaxTokenProcessingTime; // Interrupt parsing during token procesing after # of microseconds PRInt32 mDynamicIntervalSwitchThreshold; // Switch between intervals when time is exceeded PRInt32 mBeginLoadTime; nsCOMPtr mObservers; void StartLayout(); void ScrollToRef(); void TryToScrollToRef(); void AddBaseTagInfo(nsIHTMLContent* aContent); nsresult ProcessLink(nsIHTMLContent* aElement, const nsAReadableString& aLinkData); nsresult ProcessStyleLink(nsIHTMLContent* aElement, const nsString& aHref, const nsString& aRel, const nsString& aTitle, const nsString& aType, const nsString& aMedia); void ProcessBaseHref(const nsAReadableString& aBaseHref); void ProcessBaseTarget(const nsAReadableString& aBaseTarget); nsresult RefreshIfEnabled(nsIViewManager* vm); // Routines for tags that require special handling nsresult ProcessATag(const nsIParserNode& aNode, nsIHTMLContent* aContent); nsresult ProcessAREATag(const nsIParserNode& aNode); nsresult ProcessBASETag(const nsIParserNode& aNode); nsresult ProcessLINKTag(const nsIParserNode& aNode); nsresult ProcessMAPTag(const nsIParserNode& aNode, nsIHTMLContent* aContent); nsresult ProcessMETATag(const nsIParserNode& aNode); nsresult ProcessSCRIPTTag(const nsIParserNode& aNode); nsresult ProcessSTYLETag(const nsIParserNode& aNode); nsresult ProcessHeaderData(nsIAtom* aHeader,const nsAReadableString& aValue,nsIHTMLContent* aContent=nsnull); nsresult ProcessHTTPHeaders(nsIChannel* aChannel); // Script processing related routines nsresult ResumeParsing(); PRBool PreEvaluateScript(); void PostEvaluateScript(); void UpdateAllContexts(); void NotifyAppend(nsIContent* aContent, PRInt32 aStartIndex); void NotifyInsert(nsIContent* aContent, nsIContent* aChildContent, PRInt32 aIndexInContainer); PRBool IsMonolithicContainer(nsHTMLTag aTag); // CanInterrupt parsing related routines nsresult AddDummyParserRequest(void); nsresult RemoveDummyParserRequest(void); #ifdef NS_DEBUG void ForceReflow(); #endif MOZ_TIMER_DECLARE(mWatch) // Measures content model creation time for current document }; //---------------------------------------------------------------------- // // DummyParserRequest // // This is a dummy request implementation that we add to the document's load // group. It ensures that EndDocumentLoad() in the docshell doesn't fire // before we've finished all of parsing and tokenizing of the document. // class DummyParserRequest : public nsIChannel { protected: DummyParserRequest(nsIHTMLContentSink* aSink); virtual ~DummyParserRequest(); static PRInt32 gRefCnt; static nsIURI* gURI; nsCOMPtr mLoadGroup; nsIHTMLContentSink* mSink; // Weak reference public: static nsresult Create(nsIRequest** aResult, nsIHTMLContentSink* aSink); NS_DECL_ISUPPORTS // nsIRequest NS_IMETHOD GetName(PRUnichar* *result) { *result = ToNewUnicode(NS_LITERAL_STRING("about:layout-dummy-request")); return NS_OK; } NS_IMETHOD IsPending(PRBool *_retval) { *_retval = PR_TRUE; return NS_OK; } NS_IMETHOD GetStatus(nsresult *status) { *status = NS_OK; return NS_OK; } NS_IMETHOD Cancel(nsresult status); NS_IMETHOD Suspend(void) { return NS_OK; } NS_IMETHOD Resume(void) { return NS_OK; } // nsIChannel NS_IMETHOD GetOriginalURI(nsIURI* *aOriginalURI) { *aOriginalURI = gURI; NS_ADDREF(*aOriginalURI); return NS_OK; } NS_IMETHOD SetOriginalURI(nsIURI* aOriginalURI) { gURI = aOriginalURI; NS_ADDREF(gURI); return NS_OK; } NS_IMETHOD GetURI(nsIURI* *aURI) { *aURI = gURI; NS_ADDREF(*aURI); return NS_OK; } NS_IMETHOD SetURI(nsIURI* aURI) { gURI = aURI; NS_ADDREF(gURI); return NS_OK; } NS_IMETHOD Open(nsIInputStream **_retval) { *_retval = nsnull; return NS_OK; } NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt) { return NS_OK; } NS_IMETHOD GetLoadFlags(nsLoadFlags *aLoadFlags) { *aLoadFlags = nsIRequest::LOAD_NORMAL; return NS_OK; } NS_IMETHOD SetLoadFlags(nsLoadFlags aLoadFlags) { return NS_OK; } NS_IMETHOD GetOwner(nsISupports * *aOwner) { *aOwner = nsnull; return NS_OK; } NS_IMETHOD SetOwner(nsISupports * aOwner) { return NS_OK; } NS_IMETHOD GetLoadGroup(nsILoadGroup * *aLoadGroup) { *aLoadGroup = mLoadGroup; NS_IF_ADDREF(*aLoadGroup); return NS_OK; } NS_IMETHOD SetLoadGroup(nsILoadGroup * aLoadGroup) { mLoadGroup = aLoadGroup; return NS_OK; } NS_IMETHOD GetNotificationCallbacks(nsIInterfaceRequestor * *aNotificationCallbacks) { *aNotificationCallbacks = nsnull; return NS_OK; } NS_IMETHOD SetNotificationCallbacks(nsIInterfaceRequestor * aNotificationCallbacks) { return NS_OK; } NS_IMETHOD GetSecurityInfo(nsISupports * *aSecurityInfo) { *aSecurityInfo = nsnull; return NS_OK; } NS_IMETHOD GetContentType(char * *aContentType) { *aContentType = nsnull; return NS_OK; } NS_IMETHOD SetContentType(const char * aContentType) { return NS_OK; } NS_IMETHOD GetContentLength(PRInt32 *aContentLength) { return NS_OK; } NS_IMETHOD SetContentLength(PRInt32 aContentLength) { return NS_OK; } }; PRInt32 DummyParserRequest::gRefCnt; nsIURI* DummyParserRequest::gURI; NS_IMPL_ADDREF(DummyParserRequest); NS_IMPL_RELEASE(DummyParserRequest); NS_IMPL_QUERY_INTERFACE2(DummyParserRequest, nsIRequest, nsIChannel); nsresult DummyParserRequest::Create(nsIRequest** aResult, nsIHTMLContentSink* aSink) { DummyParserRequest* request = new DummyParserRequest(aSink); if (!request) return NS_ERROR_OUT_OF_MEMORY; return request->QueryInterface(NS_GET_IID(nsIRequest), (void**) aResult); } DummyParserRequest::DummyParserRequest(nsIHTMLContentSink* aSink) { NS_INIT_REFCNT(); if (gRefCnt++ == 0) { nsresult rv; rv = NS_NewURI(&gURI, "about:parser-dummy-request", nsnull); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create about:parser-dummy-request"); } mSink = aSink; } DummyParserRequest::~DummyParserRequest() { if (--gRefCnt == 0) { NS_IF_RELEASE(gURI); } } NS_IMETHODIMP DummyParserRequest::Cancel(nsresult status) { // Cancel parser nsresult rv = NS_OK; HTMLContentSink* sink = NS_STATIC_CAST(HTMLContentSink*, mSink); if ((sink) && (sink->mParser)) { sink->mParser->CancelParsingEvents(); } return rv; } class SinkContext { public: SinkContext(HTMLContentSink* aSink); ~SinkContext(); // Normally when OpenContainer's are done the container is not // appended to it's parent until the container is closed. By setting // pre-append to true, the container will be appended when it is // created. void SetPreAppend(PRBool aPreAppend) { mPreAppend = aPreAppend; } nsresult Begin(nsHTMLTag aNodeType, nsIHTMLContent* aRoot, PRInt32 aNumFlushed, PRInt32 aInsertionPoint); nsresult OpenContainer(const nsIParserNode& aNode); nsresult CloseContainer(const nsIParserNode& aNode); nsresult AddLeaf(const nsIParserNode& aNode); nsresult AddLeaf(nsIHTMLContent* aContent); nsresult AddComment(const nsIParserNode& aNode); nsresult DemoteContainer(const nsIParserNode& aNode); nsresult End(); nsresult GrowStack(); nsresult AddText(const nsAReadableString& aText); nsresult FlushText(PRBool* aDidFlush = nsnull, PRBool aReleaseLast = PR_FALSE); nsresult FlushTextAndRelease(PRBool* aDidFlush = nsnull) { return FlushText(aDidFlush, PR_TRUE); } nsresult FlushTags(PRBool aNotify = PR_TRUE); PRBool IsCurrentContainer(nsHTMLTag mType); PRBool IsAncestorContainer(nsHTMLTag mType); nsIHTMLContent* GetCurrentContainer(); void DidAddContent(nsIContent* aContent, PRBool aDidNotify=PR_FALSE); void UpdateChildCounts(); HTMLContentSink* mSink; PRBool mPreAppend; PRInt32 mNotifyLevel; nsIContent* mLastTextNode; PRInt32 mLastTextNodeSize; struct Node { nsHTMLTag mType; nsIHTMLContent* mContent; PRUint32 mFlags; PRInt32 mNumFlushed; PRInt32 mInsertionPoint; }; // Node.mFlags #define APPENDED 0x1 Node* mStack; PRInt32 mStackSize; PRInt32 mStackPos; PRUnichar* mText; PRInt32 mTextLength; PRInt32 mTextSize; }; //---------------------------------------------------------------------- #ifdef NS_DEBUG void HTMLContentSink::SinkTraceNode(PRUint32 aBit, const char* aMsg, const nsIParserNode& aNode, PRInt32 aStackPos, void* aThis) { if (SINK_LOG_TEST(gSinkLogModuleInfo,aBit)) { char cbuf[40]; const char* cp; nsAutoString str; PRInt32 nt = aNode.GetNodeType(); NS_ConvertUCS2toUTF8 flat(aNode.GetText()); if ((nt > PRInt32(eHTMLTag_unknown)) && (nt < PRInt32(eHTMLTag_text)) && mParser) { nsCOMPtr dtd; mParser->GetDTD(getter_AddRefs(dtd)); dtd->IntTagToStringTag(nsHTMLTag(aNode.GetNodeType()), str); cp = str.ToCString(cbuf, sizeof(cbuf)); } else { cp = flat.get(); } PR_LogPrint("%s: this=%p node='%s' stackPos=%d", aMsg, aThis, cp, aStackPos); } } #endif /** * Helper to find identifiers that can terminate an entity * * harishd 06/23/00 * * @param aSource - Search for entity terminator in this string * @param aChar - Holds the terminated character * @param aStartOffset - Beings search, in aSource, from this offset. */ PRInt32 GetEntityTerminator(nsString& aSource,PRUnichar& aChar,PRInt32 aStartOffset=0) { PRUnichar theChar=aChar=0; PRInt32 theOffset=aStartOffset; PRInt32 theLength=aSource.Length(); PRBool found=PR_FALSE; while(theOffsetGetHTMLAttribute(keyAtom, value)) { // Get value and remove mandatory quotes GetAttributeValueAt(aNode, i, v); if (nodeType == eHTMLTag_a && keyAtom == nsHTMLAtoms::name) { NS_ConvertUCS2toUTF8 cname(v); v.Assign(NS_ConvertUTF8toUCS2(nsUnescape(NS_CONST_CAST(char *, cname.get())))); } // Add attribute to content aContent->SetAttr(kNameSpaceID_HTML, keyAtom, v, aNotify); } NS_RELEASE(keyAtom); } return NS_OK; } static void SetForm(nsIHTMLContent* aContent, nsIDOMHTMLFormElement* aForm) { nsIFormControl* formControl = nsnull; nsresult result = aContent->QueryInterface(NS_GET_IID(nsIFormControl), (void**)&formControl); if ((NS_OK == result) && formControl) { formControl->SetForm(aForm); NS_RELEASE(formControl); } } #if 0 // XXX is this logic needed by nsDOMHTMLOptionElement? void GetOptionText(const nsIParserNode& aNode, nsString& aText) { aText.SetLength(0); switch (aNode.GetTokenType()) { case eToken_text: case eToken_whitespace: case eToken_newline: aText.Append(aNode.GetText()); break; case eToken_entity: { nsAutoString tmp2(""); PRInt32 unicode = aNode.TranslateToUnicodeStr(tmp2); if (unicode < 0) { aText.Append(aNode.GetText()); } else { aText.Append(tmp2); } } break; } nsAutoString x; char* y = ToNewCString(aText); printf("foo"); } #endif static nsresult MakeContentObject(nsHTMLTag aNodeType, nsINodeInfo *aNodeInfo, nsIDOMHTMLFormElement* aForm, nsIWebShell* aWebShell, nsIHTMLContent** aResult, const nsString* aContent = nsnull, PRBool aInsideNoXXXTag = PR_FALSE); /** * Factory subroutine to create all of the html content objects. */ nsresult HTMLContentSink::CreateContentObject(const nsIParserNode& aNode, nsHTMLTag aNodeType, nsIDOMHTMLFormElement* aForm, nsIWebShell* aWebShell, nsIHTMLContent** aResult) { nsresult rv = NS_OK; // Find/create atom for the tag name nsAutoString tmp; if (eHTMLTag_userdefined == aNodeType) { tmp.Append(aNode.GetText()); tmp.ToLowerCase(); } else { nsCOMPtr dtd; rv = mParser->GetDTD(getter_AddRefs(dtd)); if (NS_SUCCEEDED(rv)) { nsAutoString str; dtd->IntTagToStringTag(aNodeType, str); tmp.Append(str); } } if (NS_SUCCEEDED(rv)) { nsCOMPtr nodeInfo; rv = mNodeInfoManager->GetNodeInfo(tmp, nsnull, kNameSpaceID_None, *getter_AddRefs(nodeInfo)); if (NS_FAILED(rv)) { return rv; } // Make the content object // XXX why is textarea not a container? nsAutoString content; if (eHTMLTag_textarea == aNodeType) { content.Assign(aNode.GetSkippedContent()); } rv = MakeContentObject(aNodeType, nodeInfo, aForm, aWebShell, aResult, &content, !!mInsideNoXXXTag); PRInt32 id; mDocument->GetAndIncrementContentID(&id); (*aResult)->SetContentID(id); } return rv; } nsresult NS_CreateHTMLElement(nsIHTMLContent** aResult, nsINodeInfo *aNodeInfo, PRBool aCaseSensitive) { nsresult rv = NS_OK; nsCOMPtr parserService = do_GetService(kParserServiceCID, &rv); if (NS_SUCCEEDED(rv)) { nsAutoString tmpName; aNodeInfo->GetName(tmpName); // Find tag in tag table PRInt32 id; nsAutoString tag; rv = parserService->HTMLStringTagToId(tmpName, &id); if (eHTMLTag_userdefined == nsHTMLTag(id)) { tag = tmpName; } else { // Create atom for tag and then create content object rv = parserService->HTMLIdToStringTag(id, tag); } if (aCaseSensitive && tag != tmpName) { rv = MakeContentObject(eHTMLTag_unknown, aNodeInfo, nsnull, nsnull, aResult); } else { nsCOMPtr atom(dont_AddRef(NS_NewAtom(tag.get()))); nsCOMPtr newName; aNodeInfo->NameChanged(atom, *getter_AddRefs(newName)); rv = MakeContentObject(nsHTMLTag(id), newName, nsnull, nsnull, aResult); } } return rv; } //---------------------------------------------------------------------- class nsHTMLElementFactory : public nsIElementFactory, public nsSupportsWeakReference { public: nsHTMLElementFactory(); virtual ~nsHTMLElementFactory(); NS_DECL_ISUPPORTS NS_IMETHOD CreateInstanceByTag(nsINodeInfo *aNodeInfo, nsIContent** aResult); static PRBool mUseXBLForms; }; PRBool nsHTMLElementFactory::mUseXBLForms = PR_FALSE; nsresult NS_NewHTMLElementFactory(nsIElementFactory** aInstancePtrResult) { NS_PRECONDITION(aInstancePtrResult, "null OUT ptr"); if (!aInstancePtrResult) { return NS_ERROR_NULL_POINTER; } nsHTMLElementFactory* it = new nsHTMLElementFactory(); if (!it) { return NS_ERROR_OUT_OF_MEMORY; } return it->QueryInterface(NS_GET_IID(nsIElementFactory), (void**)aInstancePtrResult); } nsHTMLElementFactory::nsHTMLElementFactory() { NS_INIT_REFCNT(); nsCOMPtr prefService(do_GetService(NS_PREF_CONTRACTID)); if (prefService) prefService->GetBoolPref("nglayout.debug.enable_xbl_forms", &mUseXBLForms); } nsHTMLElementFactory::~nsHTMLElementFactory() { } NS_IMPL_ISUPPORTS2(nsHTMLElementFactory, nsIElementFactory, nsISupportsWeakReference); NS_IMETHODIMP nsHTMLElementFactory::CreateInstanceByTag(nsINodeInfo *aNodeInfo, nsIContent** aResult) { NS_ENSURE_ARG_POINTER(aResult); NS_ENSURE_ARG_POINTER(aNodeInfo); nsresult rv; nsCOMPtr htmlContent; rv = NS_CreateHTMLElement(getter_AddRefs(htmlContent), aNodeInfo, PR_FALSE); nsCOMPtr content = do_QueryInterface(htmlContent); *aResult = content; NS_IF_ADDREF(*aResult); return rv; } // XXX compare switch statement against nsHTMLTags.h's list nsresult MakeContentObject(nsHTMLTag aNodeType, nsINodeInfo *aNodeInfo, nsIDOMHTMLFormElement* aForm, nsIWebShell* aWebShell, nsIHTMLContent** aResult, const nsString* aContent, PRBool aInsideNoXXXTag) { nsresult rv = NS_OK; switch (aNodeType) { case eHTMLTag_a: rv = NS_NewHTMLAnchorElement(aResult, aNodeInfo); break; case eHTMLTag_applet: rv = NS_NewHTMLAppletElement(aResult, aNodeInfo); break; case eHTMLTag_area: rv = NS_NewHTMLAreaElement(aResult, aNodeInfo); break; case eHTMLTag_base: rv = NS_NewHTMLBaseElement(aResult, aNodeInfo); break; case eHTMLTag_basefont: rv = NS_NewHTMLBaseFontElement(aResult, aNodeInfo); break; case eHTMLTag_blockquote: rv = NS_NewHTMLQuoteElement(aResult, aNodeInfo); break; case eHTMLTag_body: rv = NS_NewHTMLBodyElement(aResult, aNodeInfo); break; case eHTMLTag_br: rv = NS_NewHTMLBRElement(aResult, aNodeInfo); break; case eHTMLTag_button: if (nsHTMLElementFactory::mUseXBLForms) rv = NS_NewHTMLSpanElement(aResult, aNodeInfo); else { rv = NS_NewHTMLButtonElement(aResult, aNodeInfo); if (!aInsideNoXXXTag) { SetForm(*aResult, aForm); } } break; case eHTMLTag_caption: rv = NS_NewHTMLTableCaptionElement(aResult, aNodeInfo); break; case eHTMLTag_col: rv = NS_NewHTMLTableColElement(aResult, aNodeInfo); break; case eHTMLTag_colgroup: rv = NS_NewHTMLTableColGroupElement(aResult, aNodeInfo); break; case eHTMLTag_dir: rv = NS_NewHTMLDirectoryElement(aResult, aNodeInfo); break; case eHTMLTag_del: rv = NS_NewHTMLDelElement(aResult, aNodeInfo); break; case eHTMLTag_div: case eHTMLTag_noembed: case eHTMLTag_noframes: case eHTMLTag_noscript: case eHTMLTag_parsererror: case eHTMLTag_sourcetext: rv = NS_NewHTMLDivElement(aResult, aNodeInfo); break; case eHTMLTag_dl: rv = NS_NewHTMLDListElement(aResult, aNodeInfo); break; case eHTMLTag_embed: rv = NS_NewHTMLEmbedElement(aResult, aNodeInfo); break; case eHTMLTag_fieldset: if (nsHTMLElementFactory::mUseXBLForms) rv = NS_NewHTMLSpanElement(aResult, aNodeInfo); else { rv = NS_NewHTMLFieldSetElement(aResult, aNodeInfo); if (!aInsideNoXXXTag) { SetForm(*aResult, aForm); } } break; case eHTMLTag_font: rv = NS_NewHTMLFontElement(aResult, aNodeInfo); break; case eHTMLTag_form: // the form was already created if (aForm) { rv = aForm->QueryInterface(NS_GET_IID(nsIHTMLContent), (void**)aResult); } else { rv = NS_NewHTMLFormElement(aResult, aNodeInfo); } break; case eHTMLTag_frame: rv = NS_NewHTMLFrameElement(aResult, aNodeInfo); break; case eHTMLTag_frameset: rv = NS_NewHTMLFrameSetElement(aResult, aNodeInfo); break; case eHTMLTag_h1: case eHTMLTag_h2: case eHTMLTag_h3: case eHTMLTag_h4: case eHTMLTag_h5: case eHTMLTag_h6: rv = NS_NewHTMLHeadingElement(aResult, aNodeInfo); break; case eHTMLTag_head: rv = NS_NewHTMLHeadElement(aResult, aNodeInfo); break; case eHTMLTag_hr: rv = NS_NewHTMLHRElement(aResult, aNodeInfo); break; case eHTMLTag_html: rv = NS_NewHTMLHtmlElement(aResult, aNodeInfo); break; case eHTMLTag_iframe: rv = NS_NewHTMLIFrameElement(aResult, aNodeInfo); break; case eHTMLTag_img: rv = NS_NewHTMLImageElement(aResult, aNodeInfo); break; case eHTMLTag_input: rv = NS_NewHTMLInputElement(aResult, aNodeInfo); if (!aInsideNoXXXTag) SetForm(*aResult, aForm); break; case eHTMLTag_ins: rv = NS_NewHTMLInsElement(aResult, aNodeInfo); break; case eHTMLTag_isindex: rv = NS_NewHTMLIsIndexElement(aResult, aNodeInfo); break; case eHTMLTag_label: if (nsHTMLElementFactory::mUseXBLForms) rv = NS_NewHTMLSpanElement(aResult, aNodeInfo); else { rv = NS_NewHTMLLabelElement(aResult, aNodeInfo); if (!aInsideNoXXXTag) { SetForm(*aResult, aForm); } } break; case eHTMLTag_legend: if (nsHTMLElementFactory::mUseXBLForms) rv = NS_NewHTMLSpanElement(aResult, aNodeInfo); else { rv = NS_NewHTMLLegendElement(aResult, aNodeInfo); if (!aInsideNoXXXTag) { SetForm(*aResult, aForm); } } break; case eHTMLTag_li: rv = NS_NewHTMLLIElement(aResult, aNodeInfo); break; case eHTMLTag_link: rv = NS_NewHTMLLinkElement(aResult, aNodeInfo); break; case eHTMLTag_map: rv = NS_NewHTMLMapElement(aResult, aNodeInfo); break; case eHTMLTag_menu: rv = NS_NewHTMLMenuElement(aResult, aNodeInfo); break; case eHTMLTag_meta: rv = NS_NewHTMLMetaElement(aResult, aNodeInfo); break; case eHTMLTag_object: rv = NS_NewHTMLObjectElement(aResult, aNodeInfo); break; case eHTMLTag_ol: rv = NS_NewHTMLOListElement(aResult, aNodeInfo); break; case eHTMLTag_optgroup: if (nsHTMLElementFactory::mUseXBLForms) rv = NS_NewHTMLSpanElement(aResult, aNodeInfo); else rv = NS_NewHTMLOptGroupElement(aResult, aNodeInfo); break; case eHTMLTag_option: if (nsHTMLElementFactory::mUseXBLForms) rv = NS_NewHTMLSpanElement(aResult, aNodeInfo); else rv = NS_NewHTMLOptionElement(aResult, aNodeInfo); break; case eHTMLTag_p: rv = NS_NewHTMLParagraphElement(aResult, aNodeInfo); break; case eHTMLTag_pre: rv = NS_NewHTMLPreElement(aResult, aNodeInfo); break; case eHTMLTag_param: rv = NS_NewHTMLParamElement(aResult, aNodeInfo); break; case eHTMLTag_q: rv = NS_NewHTMLQuoteElement(aResult, aNodeInfo); break; case eHTMLTag_script: rv = NS_NewHTMLScriptElement(aResult, aNodeInfo); break; case eHTMLTag_select: if (nsHTMLElementFactory::mUseXBLForms) rv = NS_NewHTMLSpanElement(aResult, aNodeInfo); else { rv = NS_NewHTMLSelectElement(aResult, aNodeInfo); if (!aInsideNoXXXTag) { SetForm(*aResult, aForm); } } break; case eHTMLTag_spacer: rv = NS_NewHTMLSpacerElement(aResult, aNodeInfo); break; case eHTMLTag_style: rv = NS_NewHTMLStyleElement(aResult, aNodeInfo); break; case eHTMLTag_table: rv = NS_NewHTMLTableElement(aResult, aNodeInfo); break; case eHTMLTag_tbody: case eHTMLTag_thead: case eHTMLTag_tfoot: rv = NS_NewHTMLTableSectionElement(aResult, aNodeInfo); break; case eHTMLTag_td: case eHTMLTag_th: rv = NS_NewHTMLTableCellElement(aResult, aNodeInfo); break; case eHTMLTag_textarea: //if (nsHTMLElementFactory::mUseXBLForms) // rv = NS_NewHTMLSpanElement(aResult, aNodeInfo); //else { rv = NS_NewHTMLTextAreaElement(aResult, aNodeInfo); // XXX why is textarea not a container. If it were, this code would not be necessary // If the text area has some content, set it if (aContent && (aContent->Length() > 0)) { nsIDOMHTMLTextAreaElement* taElem; rv = (*aResult)->QueryInterface(NS_GET_IID(nsIDOMHTMLTextAreaElement), (void **)&taElem); if ((NS_OK == rv) && taElem) { taElem->SetDefaultValue(*aContent); NS_RELEASE(taElem); } } if (!aInsideNoXXXTag) SetForm(*aResult, aForm); //} break; case eHTMLTag_title: rv = NS_NewHTMLTitleElement(aResult, aNodeInfo); break; case eHTMLTag_tr: rv = NS_NewHTMLTableRowElement(aResult, aNodeInfo); break; case eHTMLTag_ul: rv = NS_NewHTMLUListElement(aResult, aNodeInfo); break; case eHTMLTag_wbr: rv = NS_NewHTMLWBRElement(aResult, aNodeInfo); break; case eHTMLTag_layer: case eHTMLTag_ilayer: case eHTMLTag_nolayer: case eHTMLTag_unknown: case eHTMLTag_userdefined: rv = NS_NewHTMLUnknownElement(aResult, aNodeInfo); break; default: rv = NS_NewHTMLSpanElement(aResult, aNodeInfo); break; } return rv; } //---------------------------------------------------------------------- MOZ_DECL_CTOR_COUNTER(SinkContext) SinkContext::SinkContext(HTMLContentSink* aSink) { MOZ_COUNT_CTOR(SinkContext); mSink = aSink; mPreAppend = PR_FALSE; mNotifyLevel = 0; mStack = nsnull; mStackSize = 0; mStackPos = 0; mText = nsnull; mTextLength = 0; mTextSize = 0; mLastTextNode = nsnull; mLastTextNodeSize = 0; } SinkContext::~SinkContext() { MOZ_COUNT_DTOR(SinkContext); if (nsnull != mStack) { for (PRInt32 i = 0; i < mStackPos; i++) { NS_RELEASE(mStack[i].mContent); } delete [] mStack; } if (nsnull != mText) { delete [] mText; } NS_IF_RELEASE(mLastTextNode); } nsresult SinkContext::Begin(nsHTMLTag aNodeType, nsIHTMLContent* aRoot, PRInt32 aNumFlushed, PRInt32 aInsertionPoint) { if (1 > mStackSize) { nsresult rv = GrowStack(); if (NS_OK != rv) { return rv; } } mStack[0].mType = aNodeType; mStack[0].mContent = aRoot; mStack[0].mFlags = APPENDED; mStack[0].mNumFlushed = aNumFlushed; mStack[0].mInsertionPoint = aInsertionPoint; NS_ADDREF(aRoot); mStackPos = 1; mTextLength = 0; return NS_OK; } PRBool SinkContext::IsCurrentContainer(nsHTMLTag aTag) { if (aTag == mStack[mStackPos-1].mType) { return PR_TRUE; } else { return PR_FALSE; } } PRBool SinkContext::IsAncestorContainer(nsHTMLTag aTag) { PRInt32 stackPos = mStackPos-1; while (stackPos >= 0) { if (aTag == mStack[stackPos].mType) { return PR_TRUE; } stackPos--; } return PR_FALSE; } nsIHTMLContent* SinkContext::GetCurrentContainer() { nsIHTMLContent* content = mStack[mStackPos-1].mContent; NS_ADDREF(content); return content; } void SinkContext::DidAddContent(nsIContent* aContent, PRBool aDidNotify) { PRInt32 childCount; // If there was a notification done for this content, update the // parent's notification count. if (aDidNotify && (0 < mStackPos)) { nsIContent* parent = mStack[mStackPos-1].mContent; parent->ChildCount(childCount); mStack[mStackPos-1].mNumFlushed = childCount; } if ((2 == mStackPos) && (mSink->mBody == mStack[1].mContent)) { // We just finished adding something to the body mNotifyLevel = 0; } // If we just added content to a node for which // an insertion happen, we need to do an immediate // notification for that insertion. if (!aDidNotify && (0 < mStackPos) && (mStack[mStackPos-1].mInsertionPoint != -1)) { nsIContent* parent = mStack[mStackPos-1].mContent; #ifdef NS_DEBUG // Tracing code nsAutoString str; nsCOMPtr dtd; mSink->mParser->GetDTD(getter_AddRefs(dtd)); dtd->IntTagToStringTag(nsHTMLTag(mStack[mStackPos-1].mType), str); SINK_TRACE(SINK_TRACE_REFLOW, ("SinkContext::DidAddContent: Insertion notification for parent=%s at position=%d and stackPos=%d", NS_LossyConvertUCS2toASCII(str).get(), mStack[mStackPos-1].mInsertionPoint-1, mStackPos-1)); #endif mSink->NotifyInsert(parent, aContent, mStack[mStackPos-1].mInsertionPoint-1); parent->ChildCount(childCount); mStack[mStackPos-1].mNumFlushed = childCount; } else if (!aDidNotify && mSink->IsTimeToNotify()) { SINK_TRACE(SINK_TRACE_REFLOW, ("SinkContext::DidAddContent: Notification as a result of the interval expiring; backoff count: %d", mSink->mBackoffCount)); FlushTags(PR_TRUE); } } nsresult SinkContext::OpenContainer(const nsIParserNode& aNode) { FlushTextAndRelease(); SINK_TRACE_NODE(SINK_TRACE_CALLS, "SinkContext::OpenContainer", aNode, mStackPos, mSink); nsresult rv; if (mStackPos + 1 > mStackSize) { rv = GrowStack(); if (NS_OK != rv) { return rv; } } // Create new container content object nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType()); nsIHTMLContent* content; rv = mSink->CreateContentObject(aNode, nodeType, mSink->mCurrentForm, mSink->mFrameset ? mSink->mWebShell : nsnull, &content); if (NS_OK != rv) { return rv; } if (nodeType == eHTMLTag_select) { nsCOMPtr select(do_QueryInterface(content)); if (select) { select->DoneAddingContent(PR_FALSE); } } mStack[mStackPos].mType = nodeType; mStack[mStackPos].mContent = content; mStack[mStackPos].mFlags = 0; mStack[mStackPos].mNumFlushed = 0; mStack[mStackPos].mInsertionPoint = -1; content->SetDocument(mSink->mDocument, PR_FALSE, PR_TRUE); rv = mSink->AddAttributes(aNode, content); if (mPreAppend) { NS_ASSERTION(mStackPos > 0, "container w/o parent"); if (mStackPos <= 0) { return NS_ERROR_FAILURE; } nsIHTMLContent* parent = mStack[mStackPos-1].mContent; if (mStack[mStackPos-1].mInsertionPoint != -1) { parent->InsertChildAt(content, mStack[mStackPos-1].mInsertionPoint++, PR_FALSE, PR_FALSE); } else { parent->AppendChildTo(content, PR_FALSE, PR_FALSE); } mStack[mStackPos].mFlags |= APPENDED; } mStackPos++; if (NS_OK != rv) { return rv; } if (mSink->IsMonolithicContainer(nodeType)) { mSink->mInMonolithicContainer++; } // Special handling for certain tags switch (nodeType) { case eHTMLTag_noembed: case eHTMLTag_noframes: mSink->mInsideNoXXXTag++; break; case eHTMLTag_a: mSink->ProcessATag(aNode, content); break; case eHTMLTag_form: case eHTMLTag_table: case eHTMLTag_thead: case eHTMLTag_tbody: case eHTMLTag_tfoot: case eHTMLTag_tr: case eHTMLTag_td: case eHTMLTag_th: // XXX if navigator_quirks_mode (only body in html supports background) mSink->AddBaseTagInfo(content); break; case eHTMLTag_map: mSink->ProcessMAPTag(aNode, content); break; case eHTMLTag_iframe: mSink->mNumOpenIFRAMES++; break; default: break; } return NS_OK; } nsresult SinkContext::CloseContainer(const nsIParserNode& aNode) { nsresult result = NS_OK; // Flush any collected text content. Release the last text // node to indicate that no more should be added to it. FlushTextAndRelease(); SINK_TRACE_NODE(SINK_TRACE_CALLS, "SinkContext::CloseContainer", aNode, mStackPos-1, mSink); NS_WARN_IF_FALSE(mStackPos > 0,"stack out of bounds. wrong context probably!"); if(mStackPos<=0) { return NS_OK; // Fix crash - Ref. bug 45975 or 45007 } --mStackPos; nsHTMLTag nodeType = mStack[mStackPos].mType; nsIHTMLContent* content = mStack[mStackPos].mContent; content->Compact(); // Add container to its parent if we haven't already done it if (0 == (mStack[mStackPos].mFlags & APPENDED)) { NS_ASSERTION(mStackPos > 0, "container w/o parent"); if (mStackPos <= 0) { return NS_ERROR_FAILURE; } nsIHTMLContent* parent = mStack[mStackPos-1].mContent; // If the parent has an insertion point, insert rather than // append. if (mStack[mStackPos-1].mInsertionPoint != -1) { result = parent->InsertChildAt(content, mStack[mStackPos-1].mInsertionPoint++, PR_FALSE, PR_FALSE); } else { result = parent->AppendChildTo(content, PR_FALSE, PR_FALSE); } } // If we're in a state where we do append notifications as // we go up the tree, and we're at the level where the next // notification needs to be done, do the notification. if (mNotifyLevel >= mStackPos) { PRInt32 childCount; // Check to see if new content has been added after our last // notification content->ChildCount(childCount); if (mStack[mStackPos].mNumFlushed < childCount) { #ifdef NS_DEBUG // Tracing code nsAutoString str; nsCOMPtr dtd; mSink->mParser->GetDTD(getter_AddRefs(dtd)); dtd->IntTagToStringTag(nsHTMLTag(nodeType), str); SINK_TRACE(SINK_TRACE_REFLOW, ("SinkContext::CloseContainer: reflow on notifyImmediate tag=%s newIndex=%d stackPos=%d", NS_LossyConvertUCS2toASCII(str).get(), mStack[mStackPos].mNumFlushed, mStackPos)); #endif mSink->NotifyAppend(content, mStack[mStackPos].mNumFlushed); } // Indicate that notification has now happened at this level mNotifyLevel = mStackPos-1; } if (mSink->IsMonolithicContainer(nodeType)) { --mSink->mInMonolithicContainer; } DidAddContent(content, PR_FALSE); // Special handling for certain tags switch (nodeType) { case eHTMLTag_noembed: case eHTMLTag_noframes: // Fix bug 40216 NS_ASSERTION((mSink->mInsideNoXXXTag > 0), "mInsideNoXXXTag underflow"); if (mSink->mInsideNoXXXTag > 0) { mSink->mInsideNoXXXTag--; } break; case eHTMLTag_form: { nsHTMLTag parserNodeType = nsHTMLTag(aNode.GetNodeType()); // If there's a FORM on the stack, but this close tag doesn't // close the form, then close out the form *and* close out the // next container up. This is since the parser doesn't do fix up // of invalid form nesting. When the end FORM tag comes through, // we'll ignore it. if (parserNodeType != nodeType) { result = CloseContainer(aNode); } } break; case eHTMLTag_iframe: mSink->mNumOpenIFRAMES--; break; case eHTMLTag_select: { nsCOMPtr select = do_QueryInterface(content, &result); if (NS_SUCCEEDED(result)) { result = select->DoneAddingContent(PR_TRUE); } } break; default: break; } NS_IF_RELEASE(content); #ifdef DEBUG if (mPreAppend && SINK_LOG_TEST(gSinkLogModuleInfo,SINK_ALWAYS_REFLOW)) { mSink->ForceReflow(); } #endif return result; } static void SetDocumentInChildrenOf(nsIContent* aContent, nsIDocument* aDocument) { PRInt32 i, n; aContent->ChildCount(n); for (i = 0; i < n; i++) { nsIContent* child; aContent->ChildAt(i, child); if (nsnull != child) { child->SetDocument(aDocument, PR_TRUE, PR_TRUE); NS_RELEASE(child); } } } // This method is called when a container is determined to be // non well-formed in the source content. Currently this can only // happen for forms, since the parser doesn't do fixup of forms. // The method makes the container a leaf and moves all the container's // children up a level to the container's parent. nsresult SinkContext::DemoteContainer(const nsIParserNode& aNode) { nsresult result = NS_OK; nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType()); // Search for the nearest container on the stack of the // specified type PRInt32 stackPos = mStackPos-1; while ((stackPos > 0) && (nodeType != mStack[stackPos].mType)) { stackPos--; } // If we find such a container... if (stackPos > 0) { nsIHTMLContent* container = mStack[stackPos].mContent; PRBool sync = PR_FALSE; // See if it has a parent on the stack. It should for all the // cases for which this is called, but put in a check anyway if (stackPos > 1) { nsIHTMLContent* parent = mStack[stackPos-1].mContent; PRInt32 parentCount; // If we've already flushed the demoted container, flush // what we have and do everything synchronously. If not, // the children that will be promoted will just be dealt // with later. parent->ChildCount(parentCount); if (mStack[stackPos-1].mNumFlushed == parentCount) { FlushTags(PR_TRUE); sync = PR_TRUE; } // Otherwise just append the container to the parent without // notification (if the container hasn't already been appended) else if (!(mStack[stackPos].mFlags & APPENDED)) { mSink->mInNotification++; parent->AppendChildTo(container, PR_FALSE, PR_FALSE); mSink->mInNotification--; } // Set mIsDemotingContainer here so that the above FlushTags() // calls really flushes the tags. mSink->mIsDemotingContainer = PR_TRUE; // Create a temp layoutHistoryState to store the childrens' state nsCOMPtr presShell; nsCOMPtr presContext; nsCOMPtr frameManager; nsCOMPtr tempFrameState = do_CreateInstance(kLayoutHistoryStateCID); if (mSink && mSink->mDocument) { PRInt32 ns = mSink->mDocument->GetNumberOfShells(); if (ns > 0) { mSink->mDocument->GetShellAt(0, getter_AddRefs(presShell)); if (presShell) { presShell->GetFrameManager(getter_AddRefs(frameManager)); presShell->GetPresContext(getter_AddRefs(presContext)); } } } NS_ASSERTION(presShell && frameManager && presContext && tempFrameState, "SinkContext::DemoteContainer() Error storing frame state!"); // Store children frames state before removing them from old parent nsIFrame* frame = nsnull; if (frameManager && presContext && tempFrameState) { presShell->GetPrimaryFrameFor(container, &frame); if (frame) { frameManager->CaptureFrameState(presContext, frame, tempFrameState); } } // Suspend script processing while we move children around. // We don't want to re-evaluate scripts as a result of the move. nsCOMPtr loader; if (mSink && mSink->mDocument) { mSink->mDocument->GetScriptLoader(getter_AddRefs(loader)); if (loader) { loader->Suspend(); } } if (NS_SUCCEEDED(result)) { // Move all of the demoted containers children to its parent PRInt32 i, count; container->ChildCount(count); for (i = 0; i < count && NS_SUCCEEDED(result); i++) { nsIContent* child; // Since we're removing as we go along, always get the // first child result = container->ChildAt(0, child); if (NS_SUCCEEDED(result)) { // Remove it from its old parent (the demoted container) // If the child is a form control, cache the form that contains it. // After the form control is removed from it's container, restore // it's form. nsIFormControl* childFormControl = nsnull; result = child->QueryInterface(NS_GET_IID(nsIFormControl), (void**)&childFormControl); if (NS_SUCCEEDED(result)) { // It is a form control, so get it's form and cache it. nsIDOMHTMLFormElement* formElem = nsnull; childFormControl->GetForm(&formElem); // Removing the child will set it's form control to nsnull. result = container->RemoveChildAt(0, sync); // Restore the child's form control using the cache'd pointer. childFormControl->SetForm(formElem); NS_RELEASE(childFormControl); NS_IF_RELEASE(formElem); } else { result = container->RemoveChildAt(0, sync); } if (NS_SUCCEEDED(result)) { SetDocumentInChildrenOf(child, mSink->mDocument); // Note that we're doing synchronous notifications here // since we already did notifications for all content // that's come through with the FlushTags() call so far. mSink->mInNotification++; result = parent->AppendChildTo(child, sync, PR_FALSE); mSink->mInNotification--; } NS_RELEASE(child); } } // Remove the demoted element from the context stack. while (stackPos < mStackPos-1) { mStack[stackPos].mType = mStack[stackPos+1].mType; mStack[stackPos].mContent = mStack[stackPos+1].mContent; mStack[stackPos].mFlags = mStack[stackPos+1].mFlags; stackPos++; } mStackPos--; } if (loader) { loader->Resume(); } // Restore frames state after adding it to new parent if (frameManager && presContext && tempFrameState && frame) { presShell->GetPrimaryFrameFor(parent, &frame); if (frame) { frameManager->RestoreFrameState(presContext, frame, tempFrameState); } } } NS_RELEASE(container); if (sync) { // Update child counts for everything on the stack, since // we've moved around content in the hierarchy UpdateChildCounts(); } } mSink->mIsDemotingContainer = PR_FALSE; return result; } nsresult SinkContext::AddLeaf(const nsIParserNode& aNode) { SINK_TRACE_NODE(SINK_TRACE_CALLS, "SinkContext::AddLeaf", aNode, mStackPos, mSink); nsresult rv = NS_OK; switch (aNode.GetTokenType()) { case eToken_start: { FlushTextAndRelease(); // Create new leaf content object nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType()); nsIHTMLContent* content; rv = mSink->CreateContentObject(aNode, nodeType, mSink->mCurrentForm, mSink->mWebShell, &content); if (NS_OK != rv) { return rv; } // Set the content's document content->SetDocument(mSink->mDocument, PR_FALSE, PR_TRUE); rv = mSink->AddAttributes(aNode, content); if (NS_OK != rv) { NS_RELEASE(content); return rv; } switch (nodeType) { case eHTMLTag_img: // elements with 'SRC=' case eHTMLTag_frame: case eHTMLTag_input: mSink->AddBaseTagInfo(content); break; default: break; } // Add new leaf to its parent AddLeaf(content); NS_RELEASE(content); } break; case eToken_text: case eToken_whitespace: case eToken_newline: rv = AddText(aNode.GetText()); break; case eToken_entity: { nsAutoString tmp; PRInt32 unicode = aNode.TranslateToUnicodeStr(tmp); if (unicode < 0) { rv = AddText(aNode.GetText()); } else { // Map carriage returns to newlines if(tmp.Length() > 0) { if(tmp.CharAt(0) == '\r') { tmp.AssignWithConversion("\n"); } rv = AddText(tmp); } } } break; case eToken_skippedcontent: break; } return rv; } nsresult SinkContext::AddLeaf(nsIHTMLContent* aContent) { NS_ASSERTION(mStackPos > 0, "leaf w/o container"); if (mStackPos <= 0) { return NS_ERROR_FAILURE; } nsIHTMLContent* parent = mStack[mStackPos-1].mContent; // If the parent has an insertion point, insert rather than // append. if (mStack[mStackPos-1].mInsertionPoint != -1) { parent->InsertChildAt(aContent, mStack[mStackPos-1].mInsertionPoint++, PR_FALSE, PR_FALSE); } else { parent->AppendChildTo(aContent, PR_FALSE, PR_FALSE); } DidAddContent(aContent, PR_FALSE); #ifdef DEBUG if (mPreAppend && SINK_LOG_TEST(gSinkLogModuleInfo,SINK_ALWAYS_REFLOW)) { mSink->ForceReflow(); } #endif return NS_OK; } nsresult SinkContext::AddComment(const nsIParserNode& aNode) { SINK_TRACE_NODE(SINK_TRACE_CALLS, "SinkContext::AddLeaf", aNode, mStackPos, mSink); nsIContent *comment; nsIDOMComment *domComment; nsresult result = NS_OK; FlushTextAndRelease(); result = NS_NewCommentNode(&comment); if (NS_OK == result) { result = comment->QueryInterface(NS_GET_IID(nsIDOMComment), (void **)&domComment); if (NS_OK == result) { domComment->AppendData(aNode.GetText()); NS_RELEASE(domComment); comment->SetDocument(mSink->mDocument, PR_FALSE, PR_TRUE); nsIHTMLContent* parent; if ((nsnull == mSink->mBody) && (nsnull != mSink->mHead)) { parent = mSink->mHead; } else { parent = mStack[mStackPos - 1].mContent; } // If the parent has an insertion point, insert rather than // append. if (mStack[mStackPos-1].mInsertionPoint != -1) { parent->InsertChildAt(comment, mStack[mStackPos-1].mInsertionPoint++, PR_FALSE, PR_FALSE); } else { parent->AppendChildTo(comment, PR_FALSE, PR_FALSE); } DidAddContent(comment, PR_FALSE); #ifdef DEBUG if (mPreAppend && SINK_LOG_TEST(gSinkLogModuleInfo,SINK_ALWAYS_REFLOW)) { mSink->ForceReflow(); } #endif } NS_RELEASE(comment); } return result; } nsresult SinkContext::End() { for (PRInt32 i = 0; i < mStackPos; i++) { NS_RELEASE(mStack[i].mContent); } mStackPos = 0; mTextLength = 0; return NS_OK; } nsresult SinkContext::GrowStack() { PRInt32 newSize = mStackSize * 2; if (0 == newSize) { newSize = 32; } Node* stack = new Node[newSize]; if (nsnull == stack) { return NS_ERROR_OUT_OF_MEMORY; } if (0 != mStackPos) { memcpy(stack, mStack, sizeof(Node) * mStackPos); delete [] mStack; } mStack = stack; mStackSize = newSize; return NS_OK; } /** * Add textual content to the current running text buffer. If the text buffer * overflows, flush out the text by creating a text content object and adding * it to the content tree. */ // XXX If we get a giant string grow the buffer instead of chopping it up??? nsresult SinkContext::AddText(const nsAReadableString& aText) { PRInt32 addLen = aText.Length(); if (0 == addLen) { return NS_OK; } // Create buffer when we first need it if (0 == mTextSize) { mText = new PRUnichar[4096]; if (nsnull == mText) { return NS_ERROR_OUT_OF_MEMORY; } mTextSize = 4096; } // else if (mTextLength + addLen > mTextSize) { // } // Copy data from string into our buffer; flush buffer when it fills up PRInt32 offset = 0; PRBool isLastCharCR = PR_FALSE; while (0 != addLen) { PRInt32 amount = mTextSize - mTextLength; if (amount > addLen) { amount = addLen; } if (0 == amount) { // Don't release last text node so we can add to it again nsresult rv = FlushText(); if (NS_OK != rv) { return rv; } } mTextLength += nsContentUtils::CopyNewlineNormalizedUnicodeTo(aText, offset, &mText[mTextLength], amount, isLastCharCR); offset += amount; addLen -= amount; } return NS_OK; } /** * Flush all elements that have been seen so far such that * they are visible in the tree. Specifically, make sure * that they are all added to their respective parents. * Also, do notification at the top for all content that * has been newly added so that the frame tree is complete. */ nsresult SinkContext::FlushTags(PRBool aNotify) { // If we're demoting containers right now, don't flush. if (mSink->mIsDemotingContainer) { return NS_OK; } nsresult result = NS_OK; // Don't release last text node in case we need to add to it again FlushText(); PRInt32 childCount; nsIHTMLContent* content; // Start from the top of the stack (growing upwards) and append // all content that hasn't been previously appended to the tree PRInt32 stackPos = mStackPos-1; while ((stackPos > 0) && (0 == (mStack[stackPos].mFlags & APPENDED))) { content = mStack[stackPos].mContent; nsIHTMLContent* parent = mStack[stackPos-1].mContent; // If the parent has an insertion point, insert rather than // append. if (mStack[mStackPos-1].mInsertionPoint != -1) { parent->InsertChildAt(content, mStack[mStackPos-1].mInsertionPoint++, PR_FALSE, PR_FALSE); } else { parent->AppendChildTo(content, PR_FALSE, PR_FALSE); } mStack[stackPos].mFlags |= APPENDED; stackPos--; } if (aNotify) { // Start from the base of the stack (growing upward) and do // a notification from the node that is closest to the root of // tree for any content that has been added. stackPos = 1; PRBool flushed = PR_FALSE; while (stackPos < mStackPos) { content = mStack[stackPos].mContent; content->ChildCount(childCount); if (!flushed && (mStack[stackPos].mNumFlushed < childCount)) { #ifdef NS_DEBUG // Tracing code nsAutoString str; nsCOMPtr dtd; mSink->mParser->GetDTD(getter_AddRefs(dtd)); dtd->IntTagToStringTag(nsHTMLTag(mStack[stackPos].mType), str); SINK_TRACE(SINK_TRACE_REFLOW, ("SinkContext::FlushTags: tag=%s from newindex=%d at stackPos=%d", NS_LossyConvertUCS2toASCII(str).get(), mStack[stackPos].mNumFlushed, stackPos)); #endif if ((mStack[stackPos].mInsertionPoint != -1) && (mStackPos > (stackPos+1))) { nsIContent* child = mStack[stackPos+1].mContent; mSink->NotifyInsert(content, child, mStack[stackPos].mInsertionPoint); } else { mSink->NotifyAppend(content, mStack[stackPos].mNumFlushed); } flushed = PR_TRUE; } mStack[stackPos].mNumFlushed = childCount; stackPos++; } mNotifyLevel = mStackPos-1; } return result; } void SinkContext::UpdateChildCounts() { PRInt32 childCount; nsIHTMLContent* content; // Start from the top of the stack (growing upwards) and see if // any new content has been appended. If so, we recognize that // reflows have been generated for it and we should make sure that // no further reflows occur. PRInt32 stackPos = mStackPos-1; while (stackPos > 0) { if (mStack[stackPos].mFlags & APPENDED) { content = mStack[stackPos].mContent; content->ChildCount(childCount); mStack[stackPos].mNumFlushed = childCount; } stackPos--; } mNotifyLevel = mStackPos-1; } /** * Flush any buffered text out by creating a text content object and * adding it to the content. */ nsresult SinkContext::FlushText(PRBool* aDidFlush, PRBool aReleaseLast) { nsresult rv = NS_OK; PRBool didFlush = PR_FALSE; if (0 != mTextLength) { if (mLastTextNode) { if ((mLastTextNodeSize + mTextLength) > mSink->mMaxTextRun) { mLastTextNodeSize = 0; NS_RELEASE(mLastTextNode); FlushText(aDidFlush, aReleaseLast); } else { nsCOMPtr cdata = do_QueryInterface(mLastTextNode, &rv); if (NS_SUCCEEDED(rv)) { CBufDescriptor bd(mText, PR_TRUE, mTextSize+1, mTextLength); bd.mIsConst = PR_TRUE; nsAutoString str(bd); rv = cdata->AppendData(str); mLastTextNodeSize += mTextLength; mTextLength = 0; didFlush = PR_TRUE; } } } else { nsIContent* content; rv = NS_NewTextNode(&content); if (NS_OK == rv) { // Set the content's document content->SetDocument(mSink->mDocument, PR_FALSE, PR_TRUE); // Set the text in the text node nsITextContent* text = nsnull; content->QueryInterface(NS_GET_IID(nsITextContent), (void**) &text); text->SetText(mText, mTextLength, PR_FALSE); NS_RELEASE(text); // Add text to its parent NS_ASSERTION(mStackPos > 0, "leaf w/o container"); if (mStackPos <= 0) { return NS_ERROR_FAILURE; } nsIHTMLContent* parent = mStack[mStackPos - 1].mContent; if (mStack[mStackPos-1].mInsertionPoint != -1) { parent->InsertChildAt(content, mStack[mStackPos-1].mInsertionPoint++, PR_FALSE, PR_FALSE); } else { parent->AppendChildTo(content, PR_FALSE, PR_FALSE); } mLastTextNode = content; mLastTextNodeSize += mTextLength; mTextLength = 0; didFlush = PR_TRUE; DidAddContent(content, PR_FALSE); } } } if (nsnull != aDidFlush) { *aDidFlush = didFlush; } if (aReleaseLast && mLastTextNode) { mLastTextNodeSize = 0; NS_RELEASE(mLastTextNode); } #ifdef DEBUG if (mPreAppend && didFlush && SINK_LOG_TEST(gSinkLogModuleInfo,SINK_ALWAYS_REFLOW)) { mSink->ForceReflow(); } #endif return rv; } nsresult NS_NewHTMLContentSink(nsIHTMLContentSink** aResult, nsIDocument* aDoc, nsIURI* aURL, nsIWebShell* aWebShell, nsIChannel* aChannel) { NS_PRECONDITION(nsnull != aResult, "null ptr"); if (nsnull == aResult) { return NS_ERROR_NULL_POINTER; } HTMLContentSink* it; NS_NEWXPCOM(it, HTMLContentSink); if (nsnull == it) { return NS_ERROR_OUT_OF_MEMORY; } nsresult rv = it->Init(aDoc, aURL, aWebShell,aChannel); if (NS_OK != rv) { delete it; return rv; } return it->QueryInterface(NS_GET_IID(nsIHTMLContentSink), (void **)aResult); } // Note: operator new zeros our memory HTMLContentSink::HTMLContentSink() { NS_INIT_REFCNT(); #ifdef NS_DEBUG if (nsnull == gSinkLogModuleInfo) { gSinkLogModuleInfo = PR_NewLogModule("htmlcontentsink"); } #endif mInScript = 0; mInNotification = 0; mInMonolithicContainer = 0; mInsideNoXXXTag = 0; mFlags=0; mNeedToBlockParser = PR_FALSE; mDummyParserRequest = nsnull; mBeginLoadTime = 0; mScrolledToRefAlready = PR_FALSE; } HTMLContentSink::~HTMLContentSink() { NS_IF_RELEASE(mHead); NS_IF_RELEASE(mBody); NS_IF_RELEASE(mFrameset); NS_IF_RELEASE(mRoot); if (mDocument) { mDocument->RemoveObserver(this); NS_RELEASE(mDocument); } NS_IF_RELEASE(mHTMLDocument); NS_IF_RELEASE(mDocumentURI); NS_IF_RELEASE(mDocumentBaseURL); NS_IF_RELEASE(mWebShell); NS_IF_RELEASE(mParser); NS_IF_RELEASE(mCSSLoader); NS_IF_RELEASE(mCurrentForm); NS_IF_RELEASE(mCurrentMap); if (mNotificationTimer) { mNotificationTimer->Cancel(); } PRInt32 numContexts = mContextStack.Count(); if(mCurrentContext==mHeadContext) { // Pop off the second html context if it's not done earlier mContextStack.RemoveElementAt(--numContexts); } for (PRInt32 i = 0; i < numContexts; i++) { SinkContext* sc = (SinkContext*)mContextStack.ElementAt(i); if(sc) { sc->End(); if (sc == mCurrentContext) { mCurrentContext = nsnull; } delete sc; } } if (mCurrentContext == mHeadContext) { mCurrentContext = nsnull; } if (nsnull != mCurrentContext) { delete mCurrentContext; } if (nsnull != mHeadContext) { delete mHeadContext; } if (nsnull != mTitle) { delete mTitle; } } #ifdef DEBUG NS_IMPL_ISUPPORTS7(HTMLContentSink, nsIHTMLContentSink, nsIContentSink, nsIScriptLoaderObserver, nsITimerCallback, nsICSSLoaderObserver, nsIDocumentObserver, nsIDebugDumpContent) #else NS_IMPL_ISUPPORTS6(HTMLContentSink, nsIHTMLContentSink, nsIContentSink, nsIScriptLoaderObserver, nsITimerCallback, nsICSSLoaderObserver, nsIDocumentObserver) #endif static PRBool IsScriptEnabled(nsIDocument *aDoc, nsIWebShell *aContainer) { NS_ENSURE_TRUE(aDoc && aContainer, PR_TRUE); nsCOMPtr securityManager(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID)); NS_ENSURE_TRUE(securityManager, PR_TRUE); nsCOMPtr principal; aDoc->GetPrincipal(getter_AddRefs(principal)); NS_ENSURE_TRUE(principal, PR_TRUE); nsCOMPtr globalObject; aDoc->GetScriptGlobalObject(getter_AddRefs(globalObject)); // Getting context is tricky if the document hasn't had it's GlobalObject set yet if (!globalObject) { nsCOMPtr requestor(do_QueryInterface(aContainer)); NS_ENSURE_TRUE(requestor, PR_TRUE); nsCOMPtr owner; requestor->GetInterface(NS_GET_IID(nsIScriptGlobalObjectOwner), getter_AddRefs(owner)); NS_ENSURE_TRUE(owner, PR_TRUE); owner->GetScriptGlobalObject(getter_AddRefs(globalObject)); NS_ENSURE_TRUE(globalObject, PR_TRUE); } nsCOMPtr scriptContext; globalObject->GetContext(getter_AddRefs(scriptContext)); NS_ENSURE_TRUE(scriptContext, PR_TRUE); JSContext* cx = (JSContext *) scriptContext->GetNativeContext(); NS_ENSURE_TRUE(cx, PR_TRUE); PRBool enabled = PR_TRUE; securityManager->CanExecuteScripts(cx, principal, &enabled); return enabled; } nsresult HTMLContentSink::Init(nsIDocument* aDoc, nsIURI* aURL, nsIWebShell* aContainer, nsIChannel* aChannel) { MOZ_TIMER_DEBUGLOG(("Reset and start: nsHTMLContentSink::Init(), this=%p\n", this)); MOZ_TIMER_RESET(mWatch); MOZ_TIMER_START(mWatch); NS_PRECONDITION(nsnull != aDoc, "null ptr"); NS_PRECONDITION(nsnull != aURL, "null ptr"); NS_PRECONDITION(nsnull != aContainer, "null ptr"); if ((nsnull == aDoc) || (nsnull == aURL) || (nsnull == aContainer)) { MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::Init()\n")); MOZ_TIMER_STOP(mWatch); return NS_ERROR_NULL_POINTER; } nsresult rv; mDocument = aDoc; NS_ADDREF(aDoc); aDoc->AddObserver(this); aDoc->QueryInterface(NS_GET_IID(nsIHTMLDocument), (void**)&mHTMLDocument); rv = mDocument->GetNodeInfoManager(*getter_AddRefs(mNodeInfoManager)); NS_ENSURE_SUCCESS(rv, rv); mDocumentURI = aURL; NS_ADDREF(aURL); mDocumentBaseURL = aURL; NS_ADDREF(aURL); mWebShell = aContainer; NS_ADDREF(aContainer); mObservers = nsnull; nsCOMPtr service(do_GetService(kParserServiceCID)); if (!service) { return NS_ERROR_OUT_OF_MEMORY; } service->GetTopicObservers(NS_LITERAL_STRING("text/html"), getter_AddRefs(mObservers)); nsCOMPtr loader; rv = mDocument->GetScriptLoader(getter_AddRefs(loader)); NS_ENSURE_SUCCESS(rv, rv); loader->AddObserver(this); PRBool enabled = PR_TRUE; nsCOMPtr docShell(do_QueryInterface(mWebShell)); NS_ASSERTION(docShell,"oops no docshell!"); if (docShell) { docShell->GetAllowSubframes(&enabled); if (enabled) { mFlags |= NS_SINK_FLAG_FRAMES_ENABLED; } } // Find out if scripts are enabled, if not, show