1. Factoring nsIChannel into a protocol specific part, the nsIChannel, and a socket specific, the nsITransport. 2. Derive the nsIChannel from a nsIRequest. 2. Changes the notification system from necko and the URILoader to pass the nsIRequest interface instead of nsIChannel interface. This goal stems from wanting to be able to have active AsyncRead and AsyncWrite operations on nsSocketTransport. This is desired because it would greatly simplify the task of maintaining persistent/reusable socket connections for FTP, HTTP, and Imap (and potentially other protocols). The problem with the existing nsIChannel interface is that it does not allow one to selectively suspend just one of the read or write operations while keeping the other active. The full details of the change on written up in the netlib newsgroup. r=darin@netscape.com sr=rpotts@netscape.com git-svn-id: svn://10.0.0.236/trunk@86717 18797224-902f-48f8-a5cc-f745e15eee43
5277 lines
157 KiB
C++
5277 lines
157 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* 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 Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Pierre Phaneuf <pp@ludusdesign.com>
|
|
*/
|
|
#include "nsCOMPtr.h"
|
|
#include "nsXPIDLString.h"
|
|
#include "nsIHTMLContentSink.h"
|
|
#include "nsIInterfaceRequestor.h"
|
|
#include "nsIParser.h"
|
|
#include "nsICSSStyleSheet.h"
|
|
#include "nsICSSLoader.h"
|
|
#include "nsICSSLoaderObserver.h"
|
|
#include "nsIHTMLContent.h"
|
|
#include "nsIHTMLContentContainer.h"
|
|
#include "nsIUnicharInputStream.h"
|
|
#include "nsIURL.h"
|
|
#include "nsIStreamLoader.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 "nsHTMLEntities.h"
|
|
#include "nsCRT.h"
|
|
#include "jsapi.h" // for JSVERSION_* and JS_VersionToString
|
|
#include "prtime.h"
|
|
#include "prlog.h"
|
|
|
|
#include "nsHTMLParts.h"
|
|
#include "nsIElementFactory.h"
|
|
#include "nsITextContent.h"
|
|
|
|
#include "nsIDOMText.h"
|
|
#include "nsIDOMComment.h"
|
|
#include "nsIDOMHTMLDocument.h"
|
|
#include "nsIDOMDOMImplementation.h"
|
|
#include "nsIDOMDocumentType.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 "nsLayoutUtils.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 "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 "nsReadableUtils.h"
|
|
#include "nsWeakReference.h"//nshtmlelementfactory supports weak references
|
|
|
|
#ifdef ALLOW_ASYNCH_STYLE_SHEETS
|
|
const PRBool kBlockByDefault=PR_FALSE;
|
|
#else
|
|
const PRBool kBlockByDefault=PR_TRUE;
|
|
#endif
|
|
|
|
|
|
static NS_DEFINE_CID(kPrefServiceCID, NS_PREF_CID);
|
|
static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_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
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
class SinkContext;
|
|
|
|
class HTMLContentSink : public nsIHTMLContentSink,
|
|
public nsIStreamLoaderObserver,
|
|
public nsITimerCallback,
|
|
public nsICSSLoaderObserver,
|
|
public nsIDocumentObserver,
|
|
public nsIDebugDumpContent
|
|
{
|
|
public:
|
|
HTMLContentSink();
|
|
virtual ~HTMLContentSink();
|
|
|
|
NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
|
|
|
|
nsresult Init(nsIDocument* aDoc,
|
|
nsIURI* aURL,
|
|
nsIWebShell* aContainer);
|
|
|
|
// nsISupports
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSISTREAMLOADEROBSERVER
|
|
|
|
// 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 AddComment(const nsIParserNode& aNode);
|
|
NS_IMETHOD AddProcessingInstruction(const nsIParserNode& aNode);
|
|
NS_IMETHOD AddDocTypeDecl(const nsIParserNode& aNode, PRInt32 aMode=0);
|
|
|
|
// 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 OpenNoscript(const nsIParserNode& aNode);
|
|
NS_IMETHOD CloseNoscript(const nsIParserNode& aNode);
|
|
|
|
|
|
NS_IMETHOD DoFragment(PRBool aFlag);
|
|
|
|
// nsITimerCallback
|
|
NS_IMETHOD_(void) Notify(nsITimer *timer);
|
|
|
|
// nsICSSLoaderObserver
|
|
NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet*aSheet, PRBool aNotify);
|
|
|
|
// 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 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) { return NS_OK; }
|
|
NS_IMETHOD StyleSheetRemoved(nsIDocument *aDocument,
|
|
nsIStyleSheet* aStyleSheet) { return NS_OK; }
|
|
NS_IMETHOD StyleSheetDisabledStateChanged(nsIDocument *aDocument,
|
|
nsIStyleSheet* aStyleSheet,
|
|
PRBool aDisabled) { return NS_OK; }
|
|
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; }
|
|
|
|
// nsIDebugDumpContent
|
|
NS_IMETHOD DumpContentModel();
|
|
|
|
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);
|
|
#ifdef NS_DEBUG
|
|
void SinkTraceNode(PRUint32 aBit,
|
|
const char* aMsg,
|
|
const nsIParserNode& aNode,
|
|
PRInt32 aStackPos,
|
|
void* aThis);
|
|
#endif
|
|
|
|
nsIDocument* mDocument;
|
|
nsIHTMLDocument* mHTMLDocument;
|
|
nsINodeInfoManager* mNodeInfoManager;
|
|
nsIURI* mDocumentURI;
|
|
nsIURI* mDocumentBaseURL;
|
|
nsCOMPtr<nsIURI> mScriptURI;
|
|
nsIWebShell* mWebShell;
|
|
nsIParser* mParser;
|
|
|
|
PRBool mNotifyOnTimer; // Do we notify based on time?
|
|
PRInt32 mBackoffCount; // back off timer notification after count
|
|
PRInt32 mNotificationInterval; // Notification interval in microseconds
|
|
PRTime mLastNotificationTime; // Time of last notification
|
|
nsCOMPtr<nsITimer> 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;
|
|
nsString mScriptCharset;
|
|
|
|
PRBool mLayoutStarted;
|
|
PRInt32 mInScript;
|
|
PRInt32 mInNotification;
|
|
nsIDOMHTMLFormElement* mCurrentForm;
|
|
nsIHTMLContent* mCurrentMap;
|
|
|
|
nsVoidArray mContextStack;
|
|
SinkContext* mCurrentContext;
|
|
SinkContext* mHeadContext;
|
|
PRInt32 mNumOpenIFRAMES;
|
|
|
|
nsCString mRef;
|
|
nsScrollPreference mOriginalScrollPreference;
|
|
|
|
nsString mBaseHREF;
|
|
nsString mBaseTarget;
|
|
|
|
nsString mPreferredStyle;
|
|
PRInt32 mStyleSheetCount;
|
|
nsICSSLoader* mCSSLoader;
|
|
PRInt32 mInsideNoXXXTag;
|
|
PRInt32 mInMonolithicContainer;
|
|
|
|
void StartLayout();
|
|
|
|
void ScrollToRef();
|
|
|
|
void AddBaseTagInfo(nsIHTMLContent* aContent);
|
|
|
|
nsresult ProcessLink(nsIHTMLContent* aElement, const nsString& aLinkData);
|
|
nsresult ProcessStyleLink(nsIHTMLContent* aElement,
|
|
const nsString& aHref, const nsString& aRel,
|
|
const nsString& aTitle, const nsString& aType,
|
|
const nsString& aMedia);
|
|
|
|
void ProcessBaseHref(const nsString& aBaseHref);
|
|
void ProcessBaseTarget(const nsString& 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);
|
|
|
|
// Script processing related routines
|
|
nsresult ResumeParsing();
|
|
PRBool PreEvaluateScript();
|
|
void PostEvaluateScript(PRBool aBodyPresent);
|
|
nsresult EvaluateScript(nsString& aScript,
|
|
nsIURI *aScriptURI,
|
|
PRInt32 aLineNo,
|
|
const char* aVersion);
|
|
const char* mScriptLanguageVersion;
|
|
|
|
void UpdateAllContexts();
|
|
void NotifyAppend(nsIContent* aContent,
|
|
PRInt32 aStartIndex);
|
|
void NotifyInsert(nsIContent* aContent,
|
|
nsIContent* aChildContent,
|
|
PRInt32 aIndexInContainer);
|
|
PRBool IsMonolithicContainer(nsHTMLTag aTag);
|
|
#ifdef NS_DEBUG
|
|
void ForceReflow();
|
|
#endif
|
|
|
|
MOZ_TIMER_DECLARE(mWatch) // Measures content model creation time for current document
|
|
};
|
|
|
|
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<nsIDTD> dtd;
|
|
mParser->GetDTD(getter_AddRefs(dtd));
|
|
dtd->IntTagToStringTag(nsHTMLTag(aNode.GetNodeType()), str);
|
|
cp = str.ToCString(cbuf, sizeof(cbuf));
|
|
} else {
|
|
cp = (const char*)flat;
|
|
}
|
|
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(theOffset<theLength) {
|
|
|
|
theChar=aSource[theOffset++];
|
|
|
|
if(('a'<=theChar) && (theChar<='z'))
|
|
found=PR_TRUE;
|
|
else if(('A'<=theChar) && (theChar<='Z'))
|
|
found=PR_TRUE;
|
|
else if(('0'<=theChar) && (theChar<='9'))
|
|
found=PR_TRUE;
|
|
else if('#'==theChar)
|
|
found=PR_TRUE;
|
|
else
|
|
found=PR_FALSE;
|
|
|
|
if(!found) {
|
|
aChar=theChar;
|
|
return theOffset-1;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
void HTMLContentSink::ReduceEntities(nsString& aString) {
|
|
if (mParser) {
|
|
nsCOMPtr<nsIDTD> dtd;
|
|
|
|
nsresult rv = mParser->GetDTD(getter_AddRefs(dtd));
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
// XXX Note: as coded today, this will only convert well formed
|
|
// entities. This may not be compatible enough.
|
|
// XXX there is a table in navigator that translates some numeric entities
|
|
// should we be doing that? If so then it needs to live in two places (bad)
|
|
// so we should add a translate numeric entity method from the parser...
|
|
|
|
nsAutoString theOutString;
|
|
nsAutoString theNCRStr;
|
|
|
|
PRInt32 theLen=aString.Length();
|
|
PRInt32 theAmpPos = aString.FindChar('&');
|
|
PRInt32 theStartPos=0;
|
|
PRInt32 theTermPos=-1;
|
|
PRUnichar theTermChar='\0';
|
|
const PRUnichar *theBuf=aString.GetUnicode();
|
|
|
|
nsDTDMode mode;
|
|
mHTMLDocument->GetDTDMode(mode);
|
|
|
|
while(-1!=theAmpPos) {
|
|
|
|
if(theStartPos<theAmpPos) {
|
|
//we have real data to copy to output string before doing our first conversion
|
|
theOutString.Append(&theBuf[theStartPos],theAmpPos-theStartPos);
|
|
}
|
|
|
|
theTermPos=GetEntityTerminator(aString,theTermChar,theAmpPos+1);
|
|
|
|
if(-1!=theTermPos) {
|
|
//having found a semi, copy chars between amppos and semipos;
|
|
aString.Mid(theNCRStr,theAmpPos+1,theTermPos-theAmpPos-1);
|
|
}
|
|
else {
|
|
aString.Mid(theNCRStr,theAmpPos+1,theLen-theAmpPos-1);
|
|
PRInt32 theNewAmpPos=aString.FindChar('&',PR_FALSE,theAmpPos+1);
|
|
theTermPos=(-1==theNewAmpPos) ? theLen+1 : theNewAmpPos-1;
|
|
}
|
|
|
|
theStartPos=theTermPos+1;
|
|
|
|
PRUnichar theChar=(theLen>theAmpPos+1) ? aString.CharAt(theAmpPos+1) : '\0';
|
|
PRUnichar theEntity=0;
|
|
PRInt32 theErr=0;
|
|
PRInt32 theNCRValue=0;
|
|
|
|
switch(theChar) {
|
|
case '#':
|
|
theNCRValue=theNCRStr.ToInteger(&theErr,kAutoDetect);
|
|
theEntity=PRUnichar(theNCRValue);
|
|
break;
|
|
case '{':
|
|
//XXX Write ME!
|
|
break;
|
|
default:
|
|
if(nsCRT::IsAsciiAlpha(theChar)) {
|
|
dtd->ConvertEntityToUnicode(theNCRStr, &theNCRValue);
|
|
if (eDTDMode_strict!=mode) {
|
|
// XXX - Hack - Nav. does not support entity values > 255
|
|
// on the other hand IE supports entity values > 255 with a
|
|
// semicolon. I think it's reasonable to emulate IE than Nav.
|
|
if(theNCRValue>255 && theTermChar!=';') break;
|
|
}
|
|
if(-1!=theNCRValue) {
|
|
theEntity=PRUnichar(theNCRValue);
|
|
}
|
|
}
|
|
break;
|
|
} //switch
|
|
|
|
if(theEntity) {
|
|
theOutString.Append(theEntity);
|
|
if(theTermChar!='\0' && theTermChar!='&' && theTermChar!=';') {
|
|
theOutString.Append(theTermChar);
|
|
}
|
|
}
|
|
else {
|
|
//what looked like an entity is not really one.
|
|
//so let's copy the ncrstring back to the output string
|
|
if(theTermChar!='&') { theTermPos++; }
|
|
aString.Mid(theNCRStr,theAmpPos,theTermPos-theAmpPos);
|
|
theOutString.Append(theNCRStr);
|
|
}
|
|
theAmpPos = aString.FindChar('&',PR_FALSE,theTermPos);
|
|
} //while
|
|
|
|
if(0<theOutString.Length()) {
|
|
|
|
if(theStartPos<theLen) {
|
|
aString.Mid(theNCRStr,theStartPos,theLen-theStartPos);
|
|
theOutString.Append(theNCRStr);
|
|
}
|
|
|
|
aString=theOutString;
|
|
}
|
|
|
|
} //if
|
|
} //if
|
|
}
|
|
|
|
|
|
// Temporary factory code to create content objects
|
|
|
|
void
|
|
HTMLContentSink::GetAttributeValueAt(const nsIParserNode& aNode,
|
|
PRInt32 aIndex,
|
|
nsString& aResult)
|
|
{
|
|
// Copy value
|
|
const nsString& value = aNode.GetValueAt(aIndex);
|
|
aResult.Truncate();
|
|
aResult.Append(value);
|
|
aResult.Trim("\b\r\t\n",PR_TRUE,PR_TRUE,PR_TRUE);
|
|
|
|
if ( !aResult.IsEmpty() ) {
|
|
// Strip quotes if present
|
|
PRUnichar first = aResult.First();
|
|
if ((first == '\"') || (first == '\'')) {
|
|
if (aResult.Last() == first) {
|
|
aResult.Cut(0, 1);
|
|
PRInt32 pos = aResult.Length() - 1;
|
|
if (pos >= 0) {
|
|
aResult.Cut(pos, 1);
|
|
}
|
|
} else {
|
|
// Mismatched quotes - leave them in
|
|
}
|
|
}
|
|
ReduceEntities(aResult);
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
HTMLContentSink::AddAttributes(const nsIParserNode& aNode,
|
|
nsIHTMLContent* aContent,
|
|
PRBool aNotify)
|
|
{
|
|
// Add tag attributes to the content attributes
|
|
nsAutoString k, v;
|
|
PRInt32 ac = aNode.GetAttributeCount();
|
|
for (PRInt32 i = 0; i < ac; i++) {
|
|
// Get upper-cased key
|
|
const nsAReadableString& key = aNode.GetKeyAt(i);
|
|
k.Truncate();
|
|
k.Append(key);
|
|
k.ToLowerCase();
|
|
|
|
nsIAtom* keyAtom = NS_NewAtom(k);
|
|
nsHTMLValue value;
|
|
|
|
if (NS_CONTENT_ATTR_NOT_THERE ==
|
|
aContent->GetHTMLAttribute(keyAtom, value)) {
|
|
// Get value and remove mandatory quotes
|
|
GetAttributeValueAt(aNode, i, v);
|
|
|
|
// Add attribute to content
|
|
aContent->SetAttribute(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 = aText.ToNewCString();
|
|
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<nsIDTD> 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<nsINodeInfo> 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;
|
|
}
|
|
|
|
static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID);
|
|
|
|
nsresult
|
|
NS_CreateHTMLElement(nsIHTMLContent** aResult, nsINodeInfo *aNodeInfo)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
NS_WITH_SERVICE(nsIParserService,
|
|
parserService,
|
|
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);
|
|
}
|
|
nsCOMPtr<nsIAtom> atom(dont_AddRef(NS_NewAtom(tag.GetUnicode())));
|
|
nsCOMPtr<nsINodeInfo> 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<nsIPref> prefService(do_GetService(kPrefServiceCID));
|
|
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<nsIHTMLContent> htmlContent;
|
|
rv = NS_CreateHTMLElement(getter_AddRefs(htmlContent), aNodeInfo);
|
|
nsCOMPtr<nsIContent> 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(kIHTMLContentIID, (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
|
|
char cbuf[40];
|
|
const char* cp;
|
|
nsAutoString str;
|
|
nsCOMPtr<nsIDTD> dtd;
|
|
mSink->mParser->GetDTD(getter_AddRefs(dtd));
|
|
dtd->IntTagToStringTag(nsHTMLTag(mStack[mStackPos-1].mType), str);
|
|
cp = str.ToCString(cbuf, sizeof(cbuf));
|
|
|
|
SINK_TRACE(SINK_TRACE_REFLOW,
|
|
("SinkContext::DidAddContent: Insertion notification for parent=%s at position=%d and stackPos=%d",
|
|
cp, 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<nsISelectElement> 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);
|
|
|
|
nsCOMPtr<nsIScriptGlobalObject> scriptGlobalObject;
|
|
mSink->mDocument->GetScriptGlobalObject(getter_AddRefs(scriptGlobalObject));
|
|
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);
|
|
}
|
|
else {
|
|
parent->AppendChildTo(content, 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);
|
|
}
|
|
else {
|
|
result = parent->AppendChildTo(content, 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
|
|
char cbuf[40];
|
|
const char* cp;
|
|
nsAutoString str;
|
|
nsCOMPtr<nsIDTD> dtd;
|
|
mSink->mParser->GetDTD(getter_AddRefs(dtd));
|
|
dtd->IntTagToStringTag(nsHTMLTag(nodeType), str);
|
|
cp = str.ToCString(cbuf, sizeof(cbuf));
|
|
|
|
SINK_TRACE(SINK_TRACE_REFLOW,
|
|
("SinkContext::CloseContainer: reflow on notifyImmediate tag=%s newIndex=%d stackPos=%d", cp, mStack[mStackPos].mNumFlushed, mStackPos));
|
|
#endif
|
|
mSink->NotifyAppend(content, mStack[mStackPos].mNumFlushed);
|
|
}
|
|
|
|
// Indicate that notification has now happened at this level
|
|
mNotifyLevel = mStackPos-1;
|
|
}
|
|
|
|
DidAddContent(content, PR_FALSE);
|
|
|
|
if (mSink->IsMonolithicContainer(nodeType)) {
|
|
--mSink->mInMonolithicContainer;
|
|
}
|
|
|
|
// 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<nsISelectElement> 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 (it the container hasn't already been appended)
|
|
else if (!(mStack[stackPos].mFlags & APPENDED)) {
|
|
mSink->mInNotification++;
|
|
parent->AppendChildTo(container, PR_FALSE);
|
|
mSink->mInNotification--;
|
|
}
|
|
|
|
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);
|
|
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--;
|
|
}
|
|
}
|
|
NS_RELEASE(container);
|
|
|
|
if (sync) {
|
|
// Update child counts for everything on the stack, since
|
|
// we've moved around content in the hierarchy
|
|
UpdateChildCounts();
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
else {
|
|
parent->AppendChildTo(aContent, 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);
|
|
}
|
|
else {
|
|
parent->AppendChildTo(comment, 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;
|
|
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 += nsLayoutUtils::CopyNewlineNormalizedUnicodeTo(aText,
|
|
offset,
|
|
&mText[mTextLength],
|
|
amount);
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
else {
|
|
parent->AppendChildTo(content, 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
|
|
char cbuf[40];
|
|
const char* cp;
|
|
nsAutoString str;
|
|
nsCOMPtr<nsIDTD> dtd;
|
|
mSink->mParser->GetDTD(getter_AddRefs(dtd));
|
|
dtd->IntTagToStringTag(nsHTMLTag(mStack[stackPos].mType), str);
|
|
cp = str.ToCString(cbuf, sizeof(cbuf));
|
|
|
|
SINK_TRACE(SINK_TRACE_REFLOW,
|
|
("SinkContext::FlushTags: tag=%s from newindex=%d at stackPos=%d",
|
|
cp, 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<nsIDOMCharacterData> 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(kITextContentIID, (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);
|
|
}
|
|
else {
|
|
parent->AppendChildTo(content, 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)
|
|
{
|
|
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);
|
|
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;
|
|
mNodeInfoManager = nsnull;
|
|
}
|
|
|
|
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);
|
|
|
|
NS_IF_RELEASE(mNodeInfoManager);
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS7(HTMLContentSink,
|
|
nsIHTMLContentSink,
|
|
nsIContentSink,
|
|
nsIStreamLoaderObserver,
|
|
nsITimerCallback,
|
|
nsICSSLoaderObserver,
|
|
nsIDocumentObserver,
|
|
nsIDebugDumpContent)
|
|
|
|
nsresult
|
|
HTMLContentSink::Init(nsIDocument* aDoc,
|
|
nsIURI* aURL,
|
|
nsIWebShell* aContainer)
|
|
{
|
|
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(mNodeInfoManager);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
mDocumentURI = aURL;
|
|
NS_ADDREF(aURL);
|
|
mDocumentBaseURL = aURL;
|
|
NS_ADDREF(aURL);
|
|
mWebShell = aContainer;
|
|
NS_ADDREF(aContainer);
|
|
|
|
NS_WITH_SERVICE(nsIPref, prefs, kPrefServiceCID, &rv);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::Init()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return rv;
|
|
}
|
|
|
|
mNotifyOnTimer = PR_TRUE;
|
|
prefs->GetBoolPref("content.notify.ontimer", &mNotifyOnTimer);
|
|
|
|
mBackoffCount = -1; // never
|
|
prefs->GetIntPref("content.notify.backoffcount", &mBackoffCount);
|
|
|
|
mNotificationInterval = 1000000;
|
|
prefs->GetIntPref("content.notify.interval", &mNotificationInterval);
|
|
|
|
mMaxTextRun = 8192;
|
|
prefs->GetIntPref("content.maxtextrun", &mMaxTextRun);
|
|
|
|
nsIHTMLContentContainer* htmlContainer = nsnull;
|
|
if (NS_SUCCEEDED(aDoc->QueryInterface(NS_GET_IID(nsIHTMLContentContainer), (void**)&htmlContainer))) {
|
|
htmlContainer->GetCSSLoader(mCSSLoader);
|
|
NS_RELEASE(htmlContainer);
|
|
}
|
|
|
|
// XXX this presumes HTTP header info is alread set in document
|
|
// XXX if it isn't we need to set it here...
|
|
mDocument->GetHeaderData(nsHTMLAtoms::headerDefaultStyle, mPreferredStyle);
|
|
|
|
nsCOMPtr<nsINodeInfo> nodeInfo;
|
|
rv = mNodeInfoManager->GetNodeInfo(nsHTMLAtoms::html, nsnull,
|
|
kNameSpaceID_None,
|
|
*getter_AddRefs(nodeInfo));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
// Make root part
|
|
rv = NS_NewHTMLHtmlElement(&mRoot, nodeInfo);
|
|
if (NS_OK != rv) {
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::Init()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return rv;
|
|
}
|
|
mRoot->SetDocument(mDocument, PR_FALSE, PR_TRUE);
|
|
mDocument->SetRootContent(mRoot);
|
|
|
|
// Make head part
|
|
rv = mNodeInfoManager->GetNodeInfo(NS_ConvertASCIItoUCS2("head"),
|
|
nsnull, kNameSpaceID_None,
|
|
*getter_AddRefs(nodeInfo));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = NS_NewHTMLHeadElement(&mHead, nodeInfo);
|
|
if (NS_OK != rv) {
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::Init()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return rv;
|
|
}
|
|
mRoot->AppendChildTo(mHead, PR_FALSE);
|
|
|
|
mCurrentContext = new SinkContext(this);
|
|
mCurrentContext->Begin(eHTMLTag_html, mRoot, 0, -1);
|
|
mContextStack.AppendElement(mCurrentContext);
|
|
|
|
char* spec;
|
|
(void)aURL->GetSpec(&spec);
|
|
SINK_TRACE(SINK_TRACE_CALLS,
|
|
("HTMLContentSink::Init: this=%p url='%s'",
|
|
this, spec));
|
|
nsCRT::free(spec);
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::Init()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::WillBuildModel(void)
|
|
{
|
|
// Notify document that the load is beginning
|
|
mDocument->BeginLoad();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::DidBuildModel(PRInt32 aQualityLevel)
|
|
{
|
|
// NRA Dump stopwatch stop info here
|
|
#ifdef MOZ_PERF_METRICS
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::DidBuildModel(), this=%p\n", this));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
MOZ_TIMER_LOG(("Content creation time (this=%p): ", this));
|
|
MOZ_TIMER_PRINT(mWatch);
|
|
#endif
|
|
|
|
// Cancel a timer if we had one out there
|
|
if (mNotificationTimer) {
|
|
SINK_TRACE(SINK_TRACE_REFLOW,
|
|
("HTMLContentSink::DidBuildModel: canceling notification timeout"));
|
|
mNotificationTimer->Cancel();
|
|
mNotificationTimer = 0;
|
|
}
|
|
|
|
if (nsnull == mTitle) {
|
|
nsCOMPtr<nsIDOMHTMLDocument> domDoc(do_QueryInterface(mHTMLDocument));
|
|
if (domDoc)
|
|
domDoc->SetTitle(nsString());
|
|
}
|
|
|
|
// XXX this is silly; who cares? RickG cares. It's part of the regression test. So don't bug me.
|
|
PRInt32 i, ns = mDocument->GetNumberOfShells();
|
|
for (i = 0; i < ns; i++) {
|
|
nsCOMPtr<nsIPresShell> shell(dont_AddRef(mDocument->GetShellAt(i)));
|
|
if (shell) {
|
|
nsCOMPtr<nsIViewManager> vm;
|
|
nsresult rv = shell->GetViewManager(getter_AddRefs(vm));
|
|
if(NS_SUCCEEDED(rv) && vm) {
|
|
vm->SetQuality(nsContentQuality(aQualityLevel));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Reflow the last batch of content
|
|
if (nsnull != mBody) {
|
|
SINK_TRACE(SINK_TRACE_REFLOW,
|
|
("HTMLContentSink::DidBuildModel: layout final content"));
|
|
mCurrentContext->FlushTags(PR_TRUE);
|
|
}
|
|
|
|
ScrollToRef();
|
|
|
|
mDocument->EndLoad();
|
|
|
|
// Ref. Bug 49115
|
|
// Do this hack to make sure that the parser
|
|
// doesn't get destroyed, accidently, before
|
|
// the circularity, between sink & parser, is
|
|
// actually borken.
|
|
nsCOMPtr<nsIParser> kungFuDeathGrip(mParser);
|
|
|
|
// Drop our reference to the parser to get rid of a circular
|
|
// reference.
|
|
NS_IF_RELEASE(mParser);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP_(void)
|
|
HTMLContentSink::Notify(nsITimer *timer)
|
|
{
|
|
MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::Notify()\n"));
|
|
MOZ_TIMER_START(mWatch);
|
|
#ifdef MOZ_DEBUG
|
|
PRTime now = PR_Now();
|
|
PRInt64 diff, interval;
|
|
PRInt32 delay;
|
|
|
|
LL_I2L(interval, mNotificationInterval);
|
|
LL_SUB(diff, now, mLastNotificationTime);
|
|
|
|
LL_SUB(diff, diff, interval);
|
|
LL_L2I(delay, diff);
|
|
delay /= PR_USEC_PER_MSEC;
|
|
|
|
mBackoffCount--;
|
|
SINK_TRACE(SINK_TRACE_REFLOW,
|
|
("HTMLContentSink::Notify: reflow on a timer: %d milliseconds late, backoff count: %d", delay, mBackoffCount));
|
|
#endif
|
|
if (mCurrentContext) {
|
|
mCurrentContext->FlushTags(PR_TRUE);
|
|
}
|
|
mNotificationTimer = 0;
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::Notify()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::WillInterrupt()
|
|
{
|
|
nsresult result = NS_OK;
|
|
|
|
SINK_TRACE(SINK_TRACE_CALLS,
|
|
("HTMLContentSink::WillInterrupt: this=%p", this));
|
|
#ifdef SINK_NO_INCREMENTAL
|
|
return NS_OK;
|
|
#else
|
|
if (mNotifyOnTimer && mLayoutStarted) {
|
|
if (mBackoffCount && !mInMonolithicContainer) {
|
|
PRTime now = PR_Now();
|
|
PRInt64 interval, diff;
|
|
PRInt32 delay;
|
|
|
|
LL_I2L(interval, mNotificationInterval);
|
|
LL_SUB(diff, now, mLastNotificationTime);
|
|
|
|
// If it's already time for us to have a notification
|
|
if (LL_CMP(diff, >, interval)) {
|
|
mBackoffCount--;
|
|
SINK_TRACE(SINK_TRACE_REFLOW,
|
|
("HTMLContentSink::WillInterrupt: flushing tags since we've run out time; backoff count: %d", mBackoffCount));
|
|
result = mCurrentContext->FlushTags(PR_TRUE);
|
|
}
|
|
else {
|
|
// If the time since the last notification is less than
|
|
// the expected interval but positive, set a timer up for the remaining
|
|
// interval.
|
|
if (LL_CMP(diff, >, LL_ZERO)) {
|
|
LL_SUB(diff, interval, diff);
|
|
LL_L2I(delay, diff);
|
|
}
|
|
// Else set up a timer for the expected interval
|
|
else {
|
|
delay = mNotificationInterval;
|
|
}
|
|
|
|
// Convert to milliseconds
|
|
delay /= PR_USEC_PER_MSEC;
|
|
|
|
// Cancel a timer if we had one out there
|
|
if (mNotificationTimer) {
|
|
SINK_TRACE(SINK_TRACE_REFLOW,
|
|
("HTMLContentSink::WillInterrupt: canceling notification timeout"));
|
|
mNotificationTimer->Cancel();
|
|
}
|
|
|
|
mNotificationTimer = do_CreateInstance("@mozilla.org/timer;1", &result);
|
|
if (NS_SUCCEEDED(result)) {
|
|
SINK_TRACE(SINK_TRACE_REFLOW,
|
|
("HTMLContentSink::WillInterrupt: setting up timer with delay %d", delay));
|
|
|
|
result = mNotificationTimer->Init(this, delay);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
SINK_TRACE(SINK_TRACE_REFLOW,
|
|
("HTMLContentSink::WillInterrupt: flushing tags unconditionally"));
|
|
result = mCurrentContext->FlushTags(PR_TRUE);
|
|
}
|
|
|
|
return result;
|
|
#endif
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::WillResume()
|
|
{
|
|
SINK_TRACE(SINK_TRACE_CALLS,
|
|
("HTMLContentSink::WillResume: this=%p", this));
|
|
// Cancel a timer if we had one out there
|
|
if (mNotificationTimer) {
|
|
SINK_TRACE(SINK_TRACE_REFLOW,
|
|
("HTMLContentSink::WillResume: canceling notification timeout"));
|
|
mNotificationTimer->Cancel();
|
|
mNotificationTimer = 0;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::SetParser(nsIParser* aParser)
|
|
{
|
|
NS_IF_RELEASE(mParser);
|
|
mParser = aParser;
|
|
NS_IF_ADDREF(mParser);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::BeginContext(PRInt32 aPosition)
|
|
{
|
|
MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::BeginContext()\n"));
|
|
MOZ_TIMER_START(mWatch);
|
|
NS_PRECONDITION(aPosition > -1, "out of bounds");
|
|
|
|
// Create new context
|
|
SinkContext* sc = new SinkContext(this);
|
|
if (nsnull == sc) {
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::BeginContext()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
NS_ASSERTION(mCurrentContext != nsnull," Non-existing context");
|
|
if (mCurrentContext == nsnull) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
// Flush everything in the current context so that we don't have
|
|
// to worry about insertions resulting in inconsistent frame creation.
|
|
mCurrentContext->FlushTags(PR_TRUE);
|
|
|
|
PRInt32 insertionPoint = -1;
|
|
nsHTMLTag nodeType = mCurrentContext->mStack[aPosition].mType;
|
|
nsIHTMLContent* content = mCurrentContext->mStack[aPosition].mContent;
|
|
// If the content under which the new context is created
|
|
// has a child on the stack, the insertion point is
|
|
// before the last child.
|
|
if (aPosition < (mCurrentContext->mStackPos-1)) {
|
|
content->ChildCount(insertionPoint);
|
|
insertionPoint--;
|
|
}
|
|
sc->Begin(nodeType,
|
|
content,
|
|
mCurrentContext->mStack[aPosition].mNumFlushed,
|
|
insertionPoint);
|
|
NS_ADDREF(sc->mSink);
|
|
|
|
mContextStack.AppendElement(mCurrentContext);
|
|
mCurrentContext = sc;
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::BeginContext()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::EndContext(PRInt32 aPosition)
|
|
{
|
|
MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::EndContext()\n"));
|
|
MOZ_TIMER_START(mWatch);
|
|
NS_PRECONDITION(mCurrentContext != nsnull && aPosition > -1, "non-existing context");
|
|
|
|
PRInt32 n = mContextStack.Count() - 1;
|
|
SinkContext* sc = (SinkContext*) mContextStack.ElementAt(n);
|
|
|
|
NS_ASSERTION(sc->mStack[aPosition].mType == mCurrentContext->mStack[0].mType,"ending a wrong context");
|
|
|
|
mCurrentContext->FlushTextAndRelease();
|
|
|
|
sc->mStack[aPosition].mNumFlushed = mCurrentContext->mStack[0].mNumFlushed;
|
|
|
|
for(PRInt32 i=0; i<mCurrentContext->mStackPos; i++)
|
|
NS_IF_RELEASE(mCurrentContext->mStack[i].mContent);
|
|
delete [] mCurrentContext->mStack;
|
|
mCurrentContext->mStack = nsnull;
|
|
mCurrentContext->mStackPos = 0;
|
|
mCurrentContext->mStackSize = 0;
|
|
if(mCurrentContext->mText != nsnull)
|
|
delete [] mCurrentContext->mText;
|
|
mCurrentContext->mText = nsnull;
|
|
mCurrentContext->mTextLength = 0;
|
|
mCurrentContext->mTextSize = 0;
|
|
NS_IF_RELEASE(mCurrentContext->mSink);
|
|
delete mCurrentContext;
|
|
|
|
mCurrentContext = sc;
|
|
mContextStack.RemoveElementAt(n);
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::EndContext()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::SetTitle(const nsString& aValue)
|
|
{
|
|
MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::SetTitle()\n"));
|
|
MOZ_TIMER_START(mWatch);
|
|
NS_ASSERTION(mCurrentContext == mHeadContext, "SetTitle not in head");
|
|
|
|
if (nsnull == mTitle) {
|
|
mTitle = new nsString(aValue);
|
|
}
|
|
else {
|
|
// If the title was already set then don't try to overwrite it
|
|
// when a new title is encountered - For backwards compatiblity
|
|
//*mTitle = aValue;
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::SetTitle()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return NS_OK;
|
|
}
|
|
ReduceEntities(*mTitle);
|
|
mTitle->CompressWhitespace(PR_TRUE, PR_TRUE);
|
|
|
|
nsCOMPtr<nsIDOMHTMLDocument> domDoc(do_QueryInterface(mHTMLDocument));
|
|
if (domDoc)
|
|
domDoc->SetTitle(*mTitle);
|
|
|
|
nsCOMPtr<nsINodeInfo> nodeInfo;
|
|
nsresult rv = mNodeInfoManager->GetNodeInfo(nsHTMLAtoms::title, nsnull,
|
|
kNameSpaceID_None,
|
|
*getter_AddRefs(nodeInfo));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsIHTMLContent* it = nsnull;
|
|
rv = NS_NewHTMLTitleElement(&it, nodeInfo);
|
|
if (NS_OK == rv) {
|
|
nsIContent* text;
|
|
rv = NS_NewTextNode(&text);
|
|
if (NS_OK == rv) {
|
|
nsIDOMText* tc;
|
|
rv = text->QueryInterface(NS_GET_IID(nsIDOMText), (void**)&tc);
|
|
if (NS_OK == rv) {
|
|
tc->SetData(*mTitle);
|
|
NS_RELEASE(tc);
|
|
}
|
|
it->AppendChildTo(text, PR_FALSE);
|
|
text->SetDocument(mDocument, PR_FALSE, PR_TRUE);
|
|
NS_RELEASE(text);
|
|
}
|
|
mHead->AppendChildTo(it, PR_FALSE);
|
|
NS_RELEASE(it);
|
|
}
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::SetTitle()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::OpenHTML(const nsIParserNode& aNode)
|
|
{
|
|
|
|
MOZ_TIMER_START(mWatch);
|
|
SINK_TRACE_NODE(SINK_TRACE_CALLS,
|
|
"HTMLContentSink::OpenHTML", aNode, 0, this);
|
|
if(mRoot) {
|
|
// Add attributes to the node...if found.
|
|
PRInt32 ac = aNode.GetAttributeCount();
|
|
if(ac>0) AddAttributes(aNode,mRoot,PR_TRUE);
|
|
}
|
|
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::CloseHTML(const nsIParserNode& aNode)
|
|
{
|
|
MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::CloseHTML()\n"));
|
|
MOZ_TIMER_START(mWatch);
|
|
SINK_TRACE_NODE(SINK_TRACE_CALLS,
|
|
"HTMLContentSink::CloseHTML", aNode, 0, this);
|
|
if (nsnull != mHeadContext) {
|
|
if(mCurrentContext==mHeadContext) {
|
|
PRInt32 numContexts = mContextStack.Count();
|
|
// Pop off the second html context if it's not done earlier
|
|
mCurrentContext = (SinkContext*)mContextStack.ElementAt(--numContexts);
|
|
mContextStack.RemoveElementAt(numContexts);
|
|
}
|
|
mHeadContext->End();
|
|
delete mHeadContext;
|
|
mHeadContext = nsnull;
|
|
}
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::CloseHTML()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::OpenHead(const nsIParserNode& aNode)
|
|
{
|
|
MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::OpenHead()\n"));
|
|
MOZ_TIMER_START(mWatch);
|
|
SINK_TRACE_NODE(SINK_TRACE_CALLS,
|
|
"HTMLContentSink::OpenHead", aNode, 0, this);
|
|
nsresult rv = NS_OK;
|
|
if (nsnull == mHeadContext) {
|
|
mHeadContext = new SinkContext(this);
|
|
if (nsnull == mHeadContext) {
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::OpenHead()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
mHeadContext->SetPreAppend(PR_TRUE);
|
|
rv = mHeadContext->Begin(eHTMLTag_head, mHead, 0, -1);
|
|
if (NS_OK != rv) {
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::OpenHead()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return rv;
|
|
}
|
|
}
|
|
mContextStack.AppendElement(mCurrentContext);
|
|
mCurrentContext = mHeadContext;
|
|
|
|
if (nsnull != mHead) {
|
|
rv = AddAttributes(aNode, mHead);
|
|
}
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::OpenHead()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::CloseHead(const nsIParserNode& aNode)
|
|
{
|
|
MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::CloseHead()\n"));
|
|
MOZ_TIMER_START(mWatch);
|
|
SINK_TRACE_NODE(SINK_TRACE_CALLS,
|
|
"HTMLContentSink::CloseHead", aNode,
|
|
0, this);
|
|
PRInt32 n = mContextStack.Count() - 1;
|
|
mCurrentContext = (SinkContext*) mContextStack.ElementAt(n);
|
|
mContextStack.RemoveElementAt(n);
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::CloseHead()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::OpenBody(const nsIParserNode& aNode)
|
|
{
|
|
MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::OpenBody()\n"));
|
|
MOZ_TIMER_START(mWatch);
|
|
//NS_PRECONDITION(nsnull == mBody, "parser called OpenBody twice");
|
|
|
|
SINK_TRACE_NODE(SINK_TRACE_CALLS,
|
|
"HTMLContentSink::OpenBody", aNode,
|
|
mCurrentContext->mStackPos, this);
|
|
// Add attributes, if any, to the current BODY node
|
|
if(mBody != nsnull){
|
|
AddAttributes(aNode,mBody,PR_TRUE);
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::OpenBody()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return NS_OK;
|
|
}
|
|
|
|
// Open body. Note that we pre-append the body to the root so that
|
|
// incremental reflow during document loading will work properly.
|
|
mCurrentContext->SetPreAppend(PR_TRUE);
|
|
nsresult rv = mCurrentContext->OpenContainer(aNode);
|
|
mCurrentContext->SetPreAppend(PR_FALSE);
|
|
if (NS_OK != rv) {
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::OpenBody()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return rv;
|
|
}
|
|
mBody = mCurrentContext->mStack[mCurrentContext->mStackPos - 1].mContent;
|
|
NS_ADDREF(mBody);
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::OpenBody()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
StartLayout();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::CloseBody(const nsIParserNode& aNode)
|
|
{
|
|
MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::CloseBody()\n"));
|
|
MOZ_TIMER_START(mWatch);
|
|
SINK_TRACE_NODE(SINK_TRACE_CALLS,
|
|
"HTMLContentSink::CloseBody", aNode,
|
|
mCurrentContext->mStackPos-1, this);
|
|
|
|
PRBool didFlush;
|
|
nsresult rv = mCurrentContext->FlushTextAndRelease(&didFlush);
|
|
if (NS_OK != rv) {
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::CloseBody()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return rv;
|
|
}
|
|
// Flush out anything that's left
|
|
SINK_TRACE(SINK_TRACE_REFLOW,
|
|
("HTMLContentSink::CloseBody: layout final body content"));
|
|
mCurrentContext->FlushTags(PR_TRUE);
|
|
mCurrentContext->CloseContainer(aNode);
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::CloseBody()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::OpenForm(const nsIParserNode& aNode)
|
|
{
|
|
MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::OpenForm()\n"));
|
|
MOZ_TIMER_START(mWatch);
|
|
nsresult result = NS_OK;
|
|
nsIHTMLContent* content = nsnull;
|
|
|
|
mCurrentContext->FlushTextAndRelease();
|
|
|
|
SINK_TRACE_NODE(SINK_TRACE_CALLS,
|
|
"HTMLContentSink::OpenForm", aNode,
|
|
mCurrentContext->mStackPos, this);
|
|
|
|
// Close out previous form if it's there. If there is one
|
|
// around, it's probably because the last one wasn't well-formed.
|
|
NS_IF_RELEASE(mCurrentForm);
|
|
|
|
// Check if the parent is a table, tbody, thead, tfoot, tr, col or
|
|
// colgroup. If so, we fix up by making the form leaf content.
|
|
if (mCurrentContext->IsCurrentContainer(eHTMLTag_table) ||
|
|
mCurrentContext->IsCurrentContainer(eHTMLTag_tbody) ||
|
|
mCurrentContext->IsCurrentContainer(eHTMLTag_thead) ||
|
|
mCurrentContext->IsCurrentContainer(eHTMLTag_tfoot) ||
|
|
mCurrentContext->IsCurrentContainer(eHTMLTag_tr) ||
|
|
mCurrentContext->IsCurrentContainer(eHTMLTag_col) ||
|
|
mCurrentContext->IsCurrentContainer(eHTMLTag_colgroup)) {
|
|
|
|
nsCOMPtr<nsINodeInfo> nodeInfo;
|
|
result = mNodeInfoManager->GetNodeInfo(nsHTMLAtoms::form, nsnull,
|
|
kNameSpaceID_None,
|
|
*getter_AddRefs(nodeInfo));
|
|
NS_ENSURE_SUCCESS(result, result);
|
|
|
|
result = NS_NewHTMLFormElement(&content, nodeInfo);
|
|
if (NS_SUCCEEDED(result) && content) {
|
|
content->QueryInterface(NS_GET_IID(nsIDOMHTMLFormElement), (void**)&mCurrentForm);
|
|
NS_RELEASE(content);
|
|
}
|
|
|
|
result = AddLeaf(aNode);
|
|
}
|
|
else {
|
|
// Otherwise the form can be a content parent.
|
|
result = mCurrentContext->OpenContainer(aNode);
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
content = mCurrentContext->GetCurrentContainer();
|
|
if (nsnull != content) {
|
|
result = content->QueryInterface(NS_GET_IID(nsIDOMHTMLFormElement),
|
|
(void**)&mCurrentForm);
|
|
NS_RELEASE(content);
|
|
}
|
|
}
|
|
}
|
|
|
|
// add the form to the document
|
|
if (mCurrentForm) {
|
|
mHTMLDocument->AddForm(mCurrentForm);
|
|
}
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::OpenForm()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return result;
|
|
}
|
|
|
|
// XXX MAYBE add code to place close form tag into the content model
|
|
// for navigator layout compatability.
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::CloseForm(const nsIParserNode& aNode)
|
|
{
|
|
MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::CloseForm()\n"));
|
|
MOZ_TIMER_START(mWatch);
|
|
nsresult result = NS_OK;
|
|
|
|
mCurrentContext->FlushTextAndRelease();
|
|
|
|
SINK_TRACE_NODE(SINK_TRACE_CALLS,
|
|
"HTMLContentSink::CloseForm", aNode,
|
|
mCurrentContext->mStackPos-1, this);
|
|
|
|
if (nsnull != mCurrentForm) {
|
|
// Check if this is a well-formed form
|
|
if (mCurrentContext->IsCurrentContainer(eHTMLTag_form)) {
|
|
result = mCurrentContext->CloseContainer(aNode);
|
|
}
|
|
else if (mCurrentContext->IsAncestorContainer(eHTMLTag_form)) {
|
|
result = mCurrentContext->DemoteContainer(aNode);
|
|
}
|
|
NS_RELEASE(mCurrentForm);
|
|
}
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::CloseForm()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return result;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::OpenFrameset(const nsIParserNode& aNode)
|
|
{
|
|
MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::OpenFrameset()\n"));
|
|
MOZ_TIMER_START(mWatch);
|
|
SINK_TRACE_NODE(SINK_TRACE_CALLS,
|
|
"HTMLContentSink::OpenFrameset", aNode,
|
|
mCurrentContext->mStackPos, this);
|
|
|
|
nsresult rv = mCurrentContext->OpenContainer(aNode);
|
|
if ((NS_OK == rv) && (nsnull == mFrameset)) {
|
|
mFrameset = mCurrentContext->mStack[mCurrentContext->mStackPos-1].mContent;
|
|
NS_ADDREF(mFrameset);
|
|
}
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::OpenFrameset()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::CloseFrameset(const nsIParserNode& aNode)
|
|
{
|
|
MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::CloseFrameset()\n"));
|
|
MOZ_TIMER_START(mWatch);
|
|
SINK_TRACE_NODE(SINK_TRACE_CALLS,
|
|
"HTMLContentSink::CloseFrameset", aNode,
|
|
mCurrentContext->mStackPos-1, this);
|
|
|
|
SinkContext* sc = mCurrentContext;
|
|
nsIHTMLContent* fs = sc->mStack[sc->mStackPos-1].mContent;
|
|
PRBool done = fs == mFrameset;
|
|
nsresult rv = sc->CloseContainer(aNode);
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::CloseFrameset()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
if (done) {
|
|
StartLayout();
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::OpenMap(const nsIParserNode& aNode)
|
|
{
|
|
MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::OpenMap()\n"));
|
|
MOZ_TIMER_START(mWatch);
|
|
nsresult rv = NS_OK;
|
|
SINK_TRACE_NODE(SINK_TRACE_CALLS,
|
|
"HTMLContentSink::OpenMap", aNode,
|
|
mCurrentContext->mStackPos, this);
|
|
// We used to treat MAP elements specially (i.e. they were
|
|
// only parent elements for AREAs), but we don't anymore.
|
|
// HTML 4.0 says that MAP elements can have block content
|
|
// as children.
|
|
rv = mCurrentContext->OpenContainer(aNode);
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::OpenMap()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::CloseMap(const nsIParserNode& aNode)
|
|
{
|
|
MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::CloseMap()\n"));
|
|
MOZ_TIMER_START(mWatch);
|
|
nsresult rv = NS_OK;
|
|
SINK_TRACE_NODE(SINK_TRACE_CALLS,
|
|
"HTMLContentSink::CloseMap", aNode,
|
|
mCurrentContext->mStackPos-1, this);
|
|
NS_IF_RELEASE(mCurrentMap);
|
|
|
|
rv = mCurrentContext->CloseContainer(aNode);
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::CloseMap()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* From the pref determine if the noscript content should be
|
|
* processed or not. If java script is enabled then inform
|
|
* DTD that the content should be treated as an alternate content,i.e.,
|
|
* the content should not be treated as a regular content.
|
|
*
|
|
* harishd 08/24/00
|
|
* @param aNode - The noscript node
|
|
* return NS_OK if succeeded else ERROR
|
|
*/
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::OpenNoscript(const nsIParserNode& aNode) {
|
|
nsresult result=NS_OK;
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::OpenNoscript()\n"));
|
|
MOZ_TIMER_START(mWatch);
|
|
SINK_TRACE_NODE(SINK_TRACE_CALLS,
|
|
"HTMLContentSink::OpenNoscript", aNode,
|
|
mCurrentContext->mStackPos, this);
|
|
|
|
nsCOMPtr<nsIPref> prefs(do_GetService("@mozilla.org/preferences;1", &result));
|
|
if(NS_SUCCEEDED(result)) {
|
|
PRBool jsEnabled;
|
|
result=prefs->GetBoolPref("javascript.enabled", &jsEnabled);
|
|
if(NS_SUCCEEDED(result)){
|
|
// If JS is disabled then we want to lose the noscript element
|
|
// ,and therefore don't OpenContainer, so that the noscript contents
|
|
// get handled as if noscript wasn't present.
|
|
if(jsEnabled) {
|
|
|
|
result=mCurrentContext->OpenContainer(aNode);
|
|
|
|
if(NS_SUCCEEDED(result)) {
|
|
mInsideNoXXXTag++; // To indicate that no processing should be done to this content
|
|
result=NS_HTMLPARSER_ALTERNATECONTENT; // Inform DTD that the noscript content should be treated as CDATA.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::OpenNoscript()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Close noscript and enable processing for rest of the content,
|
|
* outside noscript
|
|
*
|
|
* harishd 08/24/00
|
|
* @param aNode - The noscript node
|
|
* return NS_OK if succeeded else ERROR
|
|
*/
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::CloseNoscript(const nsIParserNode& aNode) {
|
|
|
|
// When JS is diabled this method wouldn't get called because
|
|
// noscript element will not be present then.
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::CloseNoscript()\n"));
|
|
MOZ_TIMER_START(mWatch);
|
|
SINK_TRACE_NODE(SINK_TRACE_CALLS,
|
|
"HTMLContentSink::CloseNoscript", aNode,
|
|
mCurrentContext->mStackPos-1, this);
|
|
|
|
nsresult result=mCurrentContext->CloseContainer(aNode);
|
|
if(NS_SUCCEEDED(result)) {
|
|
NS_ASSERTION((mInsideNoXXXTag > -1), "mInsideNoXXXTag underflow");
|
|
if (mInsideNoXXXTag > 0) {
|
|
mInsideNoXXXTag--;
|
|
}
|
|
}
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::CloseNoscript()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return result;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::OpenContainer(const nsIParserNode& aNode)
|
|
{
|
|
MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::OpenContainer()\n"));
|
|
MOZ_TIMER_START(mWatch);
|
|
nsresult rv = NS_OK;
|
|
// XXX work around parser bug
|
|
if (eHTMLTag_frameset == aNode.GetNodeType()) {
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::OpenContainer()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return OpenFrameset(aNode);
|
|
}
|
|
rv = mCurrentContext->OpenContainer(aNode);
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::OpenContainer()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::CloseContainer(const nsIParserNode& aNode)
|
|
{
|
|
MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::CloseContainer()\n"));
|
|
MOZ_TIMER_START(mWatch);
|
|
nsresult rv = NS_OK;
|
|
// XXX work around parser bug
|
|
if (eHTMLTag_frameset == aNode.GetNodeType()) {
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::CloseContainer()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return CloseFrameset(aNode);
|
|
}
|
|
rv = mCurrentContext->CloseContainer(aNode);
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::CloseContainer()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::AddLeaf(const nsIParserNode& aNode)
|
|
{
|
|
MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::AddLeaf()\n"));
|
|
MOZ_TIMER_START(mWatch);
|
|
nsresult rv;
|
|
|
|
nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
|
|
switch (nodeType) {
|
|
case eHTMLTag_area:
|
|
rv = ProcessAREATag(aNode);
|
|
break;
|
|
|
|
case eHTMLTag_base:
|
|
mCurrentContext->FlushTextAndRelease();
|
|
rv = ProcessBASETag(aNode);
|
|
break;
|
|
|
|
case eHTMLTag_link:
|
|
mCurrentContext->FlushTextAndRelease();
|
|
rv = ProcessLINKTag(aNode);
|
|
break;
|
|
|
|
case eHTMLTag_meta:
|
|
mCurrentContext->FlushTextAndRelease();
|
|
rv = ProcessMETATag(aNode);
|
|
break;
|
|
|
|
case eHTMLTag_style:
|
|
mCurrentContext->FlushTextAndRelease();
|
|
rv = ProcessSTYLETag(aNode);
|
|
break;
|
|
|
|
case eHTMLTag_script:
|
|
mCurrentContext->FlushTextAndRelease();
|
|
rv = ProcessSCRIPTTag(aNode);
|
|
break;
|
|
|
|
default:
|
|
rv = mCurrentContext->AddLeaf(aNode);
|
|
break;
|
|
}
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::AddLeaf()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* This gets called by the parsing system when we find a comment
|
|
* @update gess11/9/98
|
|
* @param aNode contains a comment token
|
|
* @return error code
|
|
*/
|
|
nsresult HTMLContentSink::AddComment(const nsIParserNode& aNode) {
|
|
MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::AddComment()\n"));
|
|
MOZ_TIMER_START(mWatch);
|
|
nsresult rv = NS_OK;
|
|
rv = mCurrentContext->AddComment(aNode);
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::AddComment()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* This gets called by the parsing system when we find a PI
|
|
* @update gess11/9/98
|
|
* @param aNode contains a comment token
|
|
* @return error code
|
|
*/
|
|
nsresult HTMLContentSink::AddProcessingInstruction(const nsIParserNode& aNode) {
|
|
nsresult result= NS_OK;
|
|
MOZ_TIMER_START(mWatch);
|
|
// Implementation of AddProcessingInstruction() should start here
|
|
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This gets called by the parser when it encounters
|
|
* a DOCTYPE declaration in the HTML document.
|
|
*/
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::AddDocTypeDecl(const nsIParserNode& aNode, PRInt32 aMode)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::AddDocTypeDecl()\n"));
|
|
MOZ_TIMER_START(mWatch);
|
|
|
|
mHTMLDocument->SetDTDMode((nsDTDMode)aMode);
|
|
|
|
nsCOMPtr<nsIDOMDocument> doc(do_QueryInterface(mHTMLDocument));
|
|
|
|
if (!doc)
|
|
return NS_OK;
|
|
|
|
nsAutoString docTypeStr(aNode.GetText());
|
|
|
|
PRInt32 publicStart = docTypeStr.Find("PUBLIC", PR_TRUE);
|
|
PRInt32 systemStart = docTypeStr.Find("SYSTEM", PR_TRUE);
|
|
nsAutoString name, publicId, systemId;
|
|
|
|
if (publicStart >= 0 || systemStart >= 0) {
|
|
/*
|
|
* If we find the keyword 'PUBLIC' after the keyword 'SYSTEM' we assume
|
|
* that we got a system id that happens to contain the string "PUBLIC"
|
|
* and we ignore that as the start of the public id.
|
|
*/
|
|
if (systemStart >= 0 && (publicStart > systemStart))
|
|
publicStart = -1;
|
|
|
|
/*
|
|
* We found 'PUBLIC' or 'SYSTEM' in the doctype, put everything before
|
|
* the first one of those in name.
|
|
*/
|
|
docTypeStr.Mid(name, 0, publicStart >= 0 ? publicStart : systemStart);
|
|
|
|
if (publicStart >= 0) {
|
|
// We did find 'PUBLIC'
|
|
docTypeStr.Mid(publicId, publicStart + 6,
|
|
docTypeStr.Length() - publicStart);
|
|
publicId.Trim(" \t\n\r");
|
|
|
|
// Strip quotes
|
|
PRUnichar ch = publicId.Length() ? publicId.First() : '\0';
|
|
|
|
if (ch == '"' || ch == '\'') {
|
|
publicId.Cut(0, 1);
|
|
|
|
PRInt32 end = publicId.FindChar(ch);
|
|
|
|
if (end < 0) {
|
|
/*
|
|
* We didn't find an end quote, then we just make sure we cut of
|
|
* the '>' on the end of the doctype declaration
|
|
*/
|
|
|
|
end = publicId.FindChar('>');
|
|
}
|
|
|
|
/*
|
|
* If we didn't find an closing quote nor a '>' we leave publicId as
|
|
* it is.
|
|
*/
|
|
if (end >= 0) {
|
|
publicId.Truncate(end);
|
|
}
|
|
} else {
|
|
// No quotes, ignore the public id
|
|
publicId.Truncate();
|
|
}
|
|
|
|
/*
|
|
* Make sure the 'SYSTEM' word we found is not inside the pubilc id
|
|
*/
|
|
PRInt32 pos = docTypeStr.Find(publicId);
|
|
|
|
if (systemStart > 0) {
|
|
if (systemStart < pos + (PRInt32)publicId.Length()) {
|
|
systemStart = docTypeStr.Find("SYSTEM", PR_TRUE,
|
|
pos + publicId.Length());
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we didn't find 'SYSTEM' we tread everything after the public id
|
|
* as the system id.
|
|
*/
|
|
if (systemStart < 0) {
|
|
systemStart = pos + publicId.Length() + 1; // 1 is the end quote
|
|
}
|
|
}
|
|
|
|
if (systemStart >= 0) {
|
|
// We either found 'SYSTEM' or we have data after the public id
|
|
docTypeStr.Mid(systemId, systemStart,
|
|
docTypeStr.Length() - systemStart);
|
|
|
|
// Strip off 'SYSTEM' if we have it.
|
|
if (systemId.EqualsWithConversion("SYSTEM", PR_TRUE, 6))
|
|
systemId.Cut(0, 6);
|
|
|
|
systemId.Trim(" \t\n\r");
|
|
|
|
// Strip quotes
|
|
PRUnichar ch = systemId.Length() ? systemId.First() : '\0';
|
|
|
|
if (ch == '"' || ch == '\'') {
|
|
systemId.Cut(0, 1);
|
|
|
|
PRInt32 end = systemId.FindChar(ch);
|
|
|
|
if (end < 0) {
|
|
/*
|
|
* We didn't find an end quote, then we just make sure we cut of the
|
|
* '>' on the end of the doctype declaration
|
|
*/
|
|
|
|
end = systemId.FindChar('>');
|
|
}
|
|
|
|
/*
|
|
* If we found an closing quote nor a '>' we truncate systemId at
|
|
* that length.
|
|
*/
|
|
if (end >= 0) {
|
|
systemId.Truncate(end);
|
|
}
|
|
} else {
|
|
systemId.Truncate();
|
|
}
|
|
}
|
|
} else {
|
|
name.Assign(docTypeStr);
|
|
}
|
|
|
|
/*
|
|
* Cut out "DOCTYPE" or "<!DOCTYPE" from the name.
|
|
*/
|
|
if (name.EqualsWithConversion("<!DOCTYPE", PR_TRUE, 9))
|
|
name.Cut(0, 9);
|
|
else if (name.EqualsWithConversion("DOCTYPE", PR_TRUE, 7))
|
|
name.Cut(0, 7);
|
|
|
|
name.Trim(" \t\n\r");
|
|
|
|
/*
|
|
* Check if name contains whitespace chars, if it does and the first char
|
|
* is not a quote, we make set the name to contain the characters before
|
|
* the whitespace
|
|
*/
|
|
PRInt32 nameEnd = 0;
|
|
|
|
if (name.IsEmpty() || (name.First() != '"' && name.First() != '\'')) {
|
|
nameEnd = name.FindCharInSet(" \n\r\t");
|
|
}
|
|
|
|
/*
|
|
* If we didn't find a public id we grab everything after the name and
|
|
* use that as public id.
|
|
*/
|
|
if (publicStart < 0) {
|
|
name.Mid(publicId, nameEnd, name.Length() - nameEnd);
|
|
publicId.Trim(" \t\n\r");
|
|
|
|
PRUnichar ch = publicId.Length() ? publicId.First() : '\0';
|
|
|
|
if (ch == '"' || ch == '\'') {
|
|
publicId.Cut(0, 1);
|
|
|
|
PRInt32 publicEnd = publicId.FindChar(ch);
|
|
|
|
if (publicEnd < 0) {
|
|
publicEnd = publicId.FindChar('>');
|
|
}
|
|
|
|
if (publicEnd < 0) {
|
|
publicEnd = publicId.Length();
|
|
}
|
|
|
|
publicId.Truncate(publicEnd);
|
|
} else {
|
|
publicId.Truncate(); // No quotes, no public id
|
|
}
|
|
}
|
|
|
|
if (nameEnd >= 0)
|
|
name.Truncate(nameEnd);
|
|
else {
|
|
nameEnd = name.FindChar('>');
|
|
|
|
if (nameEnd >= 0)
|
|
name.Truncate(nameEnd);
|
|
}
|
|
|
|
if (publicId.Length() || systemId.Length() || name.Length()) {
|
|
nsCOMPtr<nsIDOMDocumentType> oldDocType;
|
|
nsCOMPtr<nsIDOMDocumentType> docType;
|
|
|
|
doc->GetDoctype(getter_AddRefs(oldDocType));
|
|
|
|
nsCOMPtr<nsIDOMDOMImplementation> domImpl;
|
|
|
|
rv = doc->GetImplementation(getter_AddRefs(domImpl));
|
|
|
|
if (NS_FAILED(rv) || !domImpl) {
|
|
return rv;
|
|
}
|
|
|
|
if (!name.Length()) {
|
|
name.AssignWithConversion("HTML");
|
|
}
|
|
|
|
rv = domImpl->CreateDocumentType(name, publicId, systemId,
|
|
getter_AddRefs(docType));
|
|
|
|
if (NS_FAILED(rv) || !docType) {
|
|
return rv;
|
|
}
|
|
nsCOMPtr<nsIDOMNode> tmpNode;
|
|
|
|
if (oldDocType) {
|
|
/*
|
|
* If we already have a doctype we replace the old one.
|
|
*/
|
|
rv = doc->ReplaceChild(oldDocType, docType, getter_AddRefs(tmpNode));
|
|
} else {
|
|
/*
|
|
* If we don't already have one we insert it as the first child,
|
|
* this might not be 100% correct but since this is called from
|
|
* the content sink we assume that this is what we want.
|
|
*/
|
|
nsCOMPtr<nsIDOMNode> firstChild;
|
|
|
|
doc->GetFirstChild(getter_AddRefs(firstChild));
|
|
|
|
/*
|
|
* If the above fails it must be because we don't have any child
|
|
* nodes, then firstChild will be 0 and InsertBefore() will append
|
|
*/
|
|
|
|
rv = doc->InsertBefore(docType, firstChild, getter_AddRefs(tmpNode));
|
|
}
|
|
}
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::AddDocTypeDecl()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
return rv;
|
|
}
|
|
|
|
|
|
void
|
|
HTMLContentSink::StartLayout()
|
|
{
|
|
if (mLayoutStarted) {
|
|
return;
|
|
}
|
|
mLayoutStarted = PR_TRUE;
|
|
|
|
mLastNotificationTime = PR_Now();
|
|
|
|
// If it's a frameset document then disable scrolling.
|
|
// Else, reset scrolling to default settings for this shell.
|
|
// This must happen before the initial reflow, when we create the root frame
|
|
nsresult rv;
|
|
if (mWebShell) {
|
|
nsCOMPtr<nsIScrollable> scrollableContainer = do_QueryInterface(mWebShell, &rv);
|
|
if (NS_SUCCEEDED(rv) && scrollableContainer) {
|
|
if (mFrameset) {
|
|
scrollableContainer->SetCurrentScrollbarPreferences(nsIScrollable::ScrollOrientation_Y, NS_STYLE_OVERFLOW_HIDDEN);
|
|
scrollableContainer->SetCurrentScrollbarPreferences(nsIScrollable::ScrollOrientation_X, NS_STYLE_OVERFLOW_HIDDEN);
|
|
} else {
|
|
scrollableContainer->ResetScrollbarPreferences();
|
|
}
|
|
}
|
|
}
|
|
|
|
PRInt32 i, ns = mDocument->GetNumberOfShells();
|
|
for (i = 0; i < ns; i++) {
|
|
nsCOMPtr<nsIPresShell> shell(dont_AddRef(mDocument->GetShellAt(i)));
|
|
if (shell) {
|
|
// Make shell an observer for next time
|
|
shell->BeginObservingDocument();
|
|
|
|
// Resize-reflow this time
|
|
nsCOMPtr<nsIPresContext> cx;
|
|
shell->GetPresContext(getter_AddRefs(cx));
|
|
nsRect r;
|
|
cx->GetVisibleArea(r);
|
|
shell->InitialReflow(r.width, r.height);
|
|
|
|
// Now trigger a refresh
|
|
nsCOMPtr<nsIViewManager> vm;
|
|
shell->GetViewManager(getter_AddRefs(vm));
|
|
if (vm) {
|
|
RefreshIfEnabled(vm);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the document we are loading has a reference or it is a
|
|
// frameset document, disable the scroll bars on the views.
|
|
char* ref = nsnull; // init in case mDocumentURI is not a url
|
|
nsIURL* url;
|
|
rv = mDocumentURI->QueryInterface(NS_GET_IID(nsIURL), (void**)&url);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
rv = url->GetRef(&ref);
|
|
NS_RELEASE(url);
|
|
}
|
|
if (rv == NS_OK) {
|
|
if (ref)
|
|
{
|
|
mRef.Assign(ref);
|
|
nsCRT::free(ref);
|
|
}
|
|
}
|
|
|
|
if ((nsnull != ref) || mFrameset) {
|
|
// XXX support more than one presentation-shell here
|
|
|
|
// Get initial scroll preference and save it away; disable the
|
|
// scroll bars.
|
|
ns = mDocument->GetNumberOfShells();
|
|
for (i = 0; i < ns; i++) {
|
|
nsCOMPtr<nsIPresShell> shell(dont_AddRef(mDocument->GetShellAt(i)));
|
|
if (shell) {
|
|
nsCOMPtr<nsIViewManager> vm;
|
|
shell->GetViewManager(getter_AddRefs(vm));
|
|
if (vm) {
|
|
nsIView* rootView = nsnull;
|
|
vm->GetRootView(rootView);
|
|
if (nsnull != rootView) {
|
|
nsIScrollableView* sview = nsnull;
|
|
rootView->QueryInterface(NS_GET_IID(nsIScrollableView), (void**) &sview);
|
|
if (nsnull != sview) {
|
|
if (mFrameset)
|
|
mOriginalScrollPreference = nsScrollPreference_kNeverScroll;
|
|
else
|
|
sview->GetScrollPreference(mOriginalScrollPreference);
|
|
sview->SetScrollPreference(nsScrollPreference_kNeverScroll);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Convert the ref from document charset to unicode.
|
|
static nsresult CharsetConvRef(const nsString& aDocCharset, const nsCString& aRefInDocCharset, nsString& aRefInUnicode)
|
|
{
|
|
nsresult rv;
|
|
|
|
nsCOMPtr <nsIAtom> docCharsetAtom;
|
|
nsCOMPtr<nsICharsetConverterManager2> ccm2 = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
rv = ccm2->GetCharsetAtom(aDocCharset.GetUnicode(), getter_AddRefs(docCharsetAtom));
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
nsCOMPtr<nsIUnicodeDecoder> decoder;
|
|
rv = ccm2->GetUnicodeDecoder(docCharsetAtom, getter_AddRefs(decoder));
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
PRInt32 srcLen = aRefInDocCharset.Length();
|
|
PRInt32 dstLen;
|
|
rv = decoder->GetMaxLength(aRefInDocCharset, srcLen, &dstLen);
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
PRUnichar *ustr = (PRUnichar *) nsMemory::Alloc((dstLen+1) * sizeof(PRUnichar));
|
|
if (ustr)
|
|
{
|
|
rv = decoder->Convert(aRefInDocCharset, &srcLen, ustr, &dstLen);
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
ustr[dstLen] = 0;
|
|
aRefInUnicode.Assign(ustr);
|
|
}
|
|
nsMemory::Free(ustr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
HTMLContentSink::ScrollToRef()
|
|
{
|
|
if (!mRef.IsEmpty())
|
|
{
|
|
nsresult rv = NS_ERROR_FAILURE;
|
|
// We assume that the bytes are in UTF-8, as it says in the spec:
|
|
// http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
|
|
nsAutoString ref = NS_ConvertUTF8toUCS2(mRef);
|
|
|
|
PRInt32 i, ns = mDocument->GetNumberOfShells();
|
|
for (i = 0; i < ns; i++) {
|
|
nsCOMPtr<nsIPresShell> shell(dont_AddRef(mDocument->GetShellAt(i)));
|
|
if (shell) {
|
|
// Scroll to the anchor
|
|
shell->FlushPendingNotifications();
|
|
// Check an empty string which might be caused by the UTF-8 conversion
|
|
if (!ref.IsEmpty())
|
|
rv = shell->GoToAnchor(ref);
|
|
else
|
|
rv = NS_ERROR_FAILURE;
|
|
|
|
// If UTF-8 URL failed then try to assume the string as a document's charset.
|
|
if (NS_FAILED(rv))
|
|
{
|
|
nsAutoString docCharset;
|
|
rv = mDocument->GetDocumentCharacterSet(docCharset);
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
rv = CharsetConvRef(docCharset, mRef, ref);
|
|
if (NS_SUCCEEDED(rv) && !ref.IsEmpty())
|
|
rv = shell->GoToAnchor(ref);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
HTMLContentSink::AddBaseTagInfo(nsIHTMLContent* aContent)
|
|
{
|
|
if (mBaseHREF.Length() > 0) {
|
|
aContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::_baseHref, mBaseHREF, PR_FALSE);
|
|
}
|
|
if (mBaseTarget.Length() > 0) {
|
|
aContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::_baseTarget, mBaseTarget, PR_FALSE);
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
HTMLContentSink::ProcessATag(const nsIParserNode& aNode,
|
|
nsIHTMLContent* aContent)
|
|
{
|
|
AddBaseTagInfo(aContent);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
HTMLContentSink::ProcessAREATag(const nsIParserNode& aNode)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
if (nsnull != mCurrentMap) {
|
|
nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
|
|
nsIHTMLContent* area;
|
|
rv = CreateContentObject(aNode, nodeType, nsnull, nsnull, &area);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
// Set the content's document and attributes
|
|
area->SetDocument(mDocument, PR_FALSE, PR_TRUE);
|
|
rv = AddAttributes(aNode, area);
|
|
if (NS_FAILED(rv)) {
|
|
NS_RELEASE(area);
|
|
return rv;
|
|
}
|
|
|
|
AddBaseTagInfo(area); // basehref or basetarget. Fix. Bug: 30617
|
|
|
|
// Add AREA object to the current map
|
|
mCurrentMap->AppendChildTo(area, PR_FALSE);
|
|
NS_RELEASE(area);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
HTMLContentSink::ProcessBaseHref(const nsString& aBaseHref)
|
|
{
|
|
//-- Make sure this page is allowed to load this URL
|
|
nsresult rv;
|
|
NS_WITH_SERVICE(nsIScriptSecurityManager, securityManager,
|
|
NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
|
|
if (NS_FAILED(rv)) return;
|
|
nsCOMPtr<nsIURI> baseHrefURI;
|
|
rv = NS_NewURI(getter_AddRefs(baseHrefURI), aBaseHref, nsnull);
|
|
if (NS_FAILED(rv)) return;
|
|
rv = securityManager->CheckLoadURI(mDocumentBaseURL, baseHrefURI, nsIScriptSecurityManager::STANDARD);
|
|
if (NS_FAILED(rv)) return;
|
|
|
|
if (nsnull == mBody) { // still in real HEAD
|
|
mHTMLDocument->SetBaseURL(aBaseHref);
|
|
NS_RELEASE(mDocumentBaseURL);
|
|
mDocument->GetBaseURL(mDocumentBaseURL);
|
|
}
|
|
else { // NAV compatibility quirk
|
|
mBaseHREF = aBaseHref;
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
HTMLContentSink::RefreshIfEnabled(nsIViewManager* vm)
|
|
{
|
|
if (vm) {
|
|
nsCOMPtr<nsIContentViewer> contentViewer;
|
|
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(mWebShell));
|
|
NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
|
|
nsresult rv = docShell->GetContentViewer(getter_AddRefs(contentViewer));
|
|
if (NS_SUCCEEDED(rv) && (contentViewer)) {
|
|
PRBool enabled;
|
|
contentViewer->GetEnableRendering(&enabled);
|
|
if (enabled) {
|
|
vm->EnableRefresh(NS_VMREFRESH_NO_SYNC);
|
|
}
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
HTMLContentSink::ProcessBaseTarget(const nsString& aBaseTarget)
|
|
{
|
|
if (nsnull == mBody) { // still in real HEAD
|
|
mHTMLDocument->SetBaseTarget(aBaseTarget);
|
|
}
|
|
else { // NAV compatibility quirk
|
|
mBaseTarget = aBaseTarget;
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
HTMLContentSink::ProcessBASETag(const nsIParserNode& aNode)
|
|
{
|
|
nsresult result = NS_OK;
|
|
nsIHTMLContent* parent = nsnull;
|
|
|
|
if(mCurrentContext!=nsnull) {
|
|
parent=mCurrentContext->mStack[mCurrentContext->mStackPos-1].mContent;
|
|
}
|
|
|
|
if(parent!=nsnull) {
|
|
// Create content object
|
|
nsCOMPtr<nsIHTMLContent> element;
|
|
nsCOMPtr<nsINodeInfo> nodeInfo;
|
|
mNodeInfoManager->GetNodeInfo(NS_ConvertASCIItoUCS2("base"),
|
|
nsnull, kNameSpaceID_None,
|
|
*getter_AddRefs(nodeInfo));
|
|
|
|
result = NS_CreateHTMLElement(getter_AddRefs(element), nodeInfo);
|
|
if (NS_SUCCEEDED(result)) {
|
|
PRInt32 id;
|
|
mDocument->GetAndIncrementContentID(&id);
|
|
element->SetContentID(id);
|
|
|
|
// Add in the attributes and add the style content object to the
|
|
// head container.
|
|
element->SetDocument(mDocument, PR_FALSE, PR_TRUE);
|
|
result = AddAttributes(aNode, element);
|
|
if (NS_SUCCEEDED(result)) {
|
|
parent->AppendChildTo(element, PR_FALSE);
|
|
if(!mInsideNoXXXTag) {
|
|
nsAutoString value;
|
|
if (NS_CONTENT_ATTR_HAS_VALUE == element->GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::href, value)) {
|
|
ProcessBaseHref(value);
|
|
}
|
|
if (NS_CONTENT_ATTR_HAS_VALUE == element->GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::target, value)) {
|
|
ProcessBaseTarget(value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
const PRUnichar kSemiCh = PRUnichar(';');
|
|
const PRUnichar kCommaCh = PRUnichar(',');
|
|
const PRUnichar kEqualsCh = PRUnichar('=');
|
|
const PRUnichar kLessThanCh = PRUnichar('<');
|
|
const PRUnichar kGreaterThanCh = PRUnichar('>');
|
|
|
|
nsresult
|
|
HTMLContentSink::ProcessLink(nsIHTMLContent* aElement, const nsString& aLinkData)
|
|
{
|
|
nsresult result = NS_OK;
|
|
|
|
// parse link content and call process style link
|
|
nsAutoString href;
|
|
nsAutoString rel;
|
|
nsAutoString title;
|
|
nsAutoString type;
|
|
nsAutoString media;
|
|
PRBool didBlock = PR_FALSE;
|
|
|
|
nsAutoString stringList(aLinkData); // copy to work buffer
|
|
|
|
stringList.Append(kNullCh); // put an extra null at the end
|
|
|
|
PRUnichar* start = (PRUnichar*)(const PRUnichar*)stringList.GetUnicode();
|
|
PRUnichar* end = start;
|
|
PRUnichar* last = start;
|
|
PRUnichar endCh;
|
|
|
|
while (kNullCh != *start) {
|
|
while ((kNullCh != *start) && nsCRT::IsAsciiSpace(*start)) { // skip leading space
|
|
start++;
|
|
}
|
|
|
|
end = start;
|
|
last = end - 1;
|
|
|
|
while ((kNullCh != *end) && (kSemiCh != *end) && (kCommaCh != *end)) { // look for semicolon or comma
|
|
if ((kApostrophe == *end) || (kQuote == *end) ||
|
|
(kLessThanCh == *end)) { // quoted string
|
|
PRUnichar quote = *end;
|
|
if (kLessThanCh == quote) {
|
|
quote = kGreaterThanCh;
|
|
}
|
|
PRUnichar* closeQuote = (end + 1);
|
|
while ((kNullCh != *closeQuote) && (quote != *closeQuote)) {
|
|
closeQuote++; // seek closing quote
|
|
}
|
|
if (quote == *closeQuote) { // found closer
|
|
end = closeQuote; // skip to close quote
|
|
last = end - 1;
|
|
if ((kSemiCh != *(end + 1)) && (kNullCh != *(end + 1)) && (kCommaCh != *(end + 1))) {
|
|
*(++end) = kNullCh; // end string here
|
|
while ((kNullCh != *(end + 1)) && (kSemiCh != *(end + 1)) &&
|
|
(kCommaCh != *(end + 1))) { // keep going until semi or comma
|
|
end++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
end++;
|
|
last++;
|
|
}
|
|
|
|
endCh = *end;
|
|
*end = kNullCh; // end string here
|
|
|
|
if (start < end) {
|
|
if ((kLessThanCh == *start) && (kGreaterThanCh == *last)) {
|
|
*last = kNullCh;
|
|
if (0 == href.Length()) { // first one wins
|
|
href = (start + 1);
|
|
href.StripWhitespace();
|
|
}
|
|
}
|
|
else {
|
|
PRUnichar* equals = start;
|
|
while ((kNullCh != *equals) && (kEqualsCh != *equals)) {
|
|
equals++;
|
|
}
|
|
if (kNullCh != *equals) {
|
|
*equals = kNullCh;
|
|
nsAutoString attr(start);
|
|
attr.StripWhitespace();
|
|
|
|
PRUnichar* value = ++equals;
|
|
while (nsCRT::IsAsciiSpace(*value)) {
|
|
value++;
|
|
}
|
|
if (((kApostrophe == *value) || (kQuote == *value)) &&
|
|
(*value == *last)) {
|
|
*last = kNullCh;
|
|
value++;
|
|
}
|
|
|
|
if (attr.EqualsIgnoreCase("rel")) {
|
|
if (0 == rel.Length()) {
|
|
rel = value;
|
|
rel.CompressWhitespace();
|
|
}
|
|
}
|
|
else if (attr.EqualsIgnoreCase("title")) {
|
|
if (0 == title.Length()) {
|
|
title = value;
|
|
title.CompressWhitespace();
|
|
}
|
|
}
|
|
else if (attr.EqualsIgnoreCase("type")) {
|
|
if (0 == type.Length()) {
|
|
type = value;
|
|
type.StripWhitespace();
|
|
}
|
|
}
|
|
else if (attr.EqualsIgnoreCase("media")) {
|
|
if (0 == media.Length()) {
|
|
media = value;
|
|
media.ToLowerCase(); // HTML4.0 spec is inconsistent, make it case INSENSITIVE
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (kCommaCh == endCh) { // hit a comma, process what we've got so far
|
|
if (0 < href.Length()) {
|
|
result = ProcessStyleLink(aElement, href, rel, title, type, media);
|
|
if (NS_ERROR_HTMLPARSER_BLOCK == result) {
|
|
didBlock = PR_TRUE;
|
|
}
|
|
}
|
|
href.Truncate();
|
|
rel.Truncate();
|
|
title.Truncate();
|
|
type.Truncate();
|
|
media.Truncate();
|
|
}
|
|
|
|
start = ++end;
|
|
}
|
|
|
|
if (0 < href.Length()) {
|
|
result = ProcessStyleLink(aElement, href, rel, title, type, media);
|
|
if (NS_SUCCEEDED(result) && didBlock) {
|
|
result = NS_ERROR_HTMLPARSER_BLOCK;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static void ParseLinkTypes(const nsString& aTypes, nsStringArray& aResult)
|
|
{
|
|
nsAutoString stringList(aTypes); // copy to work buffer
|
|
nsAutoString subStr;
|
|
|
|
stringList.ToLowerCase();
|
|
stringList.Append(kNullCh); // put an extra null at the end
|
|
|
|
PRUnichar* start = (PRUnichar*)(const PRUnichar*)stringList.GetUnicode();
|
|
PRUnichar* end = start;
|
|
|
|
while (kNullCh != *start) {
|
|
while ((kNullCh != *start) && nsCRT::IsAsciiSpace(*start)) { // skip leading space
|
|
start++;
|
|
}
|
|
|
|
end = start;
|
|
|
|
while ((kNullCh != *end) && (! nsCRT::IsAsciiSpace(*end))) { // look for space
|
|
end++;
|
|
}
|
|
*end = kNullCh; // end string here
|
|
|
|
subStr = start;
|
|
|
|
if (0 < subStr.Length()) {
|
|
aResult.AppendString(subStr);
|
|
}
|
|
|
|
start = ++end;
|
|
}
|
|
}
|
|
|
|
static void SplitMimeType(const nsString& aValue, nsString& aType, nsString& aParams)
|
|
{
|
|
aType.Truncate();
|
|
aParams.Truncate();
|
|
PRInt32 semiIndex = aValue.FindChar(PRUnichar(';'));
|
|
if (-1 != semiIndex) {
|
|
aValue.Left(aType, semiIndex);
|
|
aValue.Right(aParams, (aValue.Length() - semiIndex) - 1);
|
|
aParams.StripWhitespace();
|
|
}
|
|
else {
|
|
aType = aValue;
|
|
}
|
|
aType.StripWhitespace();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::StyleSheetLoaded(nsICSSStyleSheet* aSheet,
|
|
PRBool aDidNotify)
|
|
{
|
|
// If there was a notification done for this style sheet, we know
|
|
// that frames have been created for all content seen so far
|
|
// (processing of a new style sheet causes recreation of the frame
|
|
// model). As a result, all contexts should update their notion of
|
|
// how much frame creation has happened.
|
|
if (aDidNotify) {
|
|
UpdateAllContexts();
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
HTMLContentSink::ProcessStyleLink(nsIHTMLContent* aElement,
|
|
const nsString& aHref, const nsString& aRel,
|
|
const nsString& aTitle, const nsString& aType,
|
|
const nsString& aMedia)
|
|
{
|
|
nsresult result = NS_OK;
|
|
|
|
if (aHref.IsEmpty()) {
|
|
// if href is empty then just bail
|
|
return result;
|
|
}
|
|
|
|
nsStringArray linkTypes;
|
|
ParseLinkTypes(aRel, linkTypes);
|
|
PRBool isAlternate = PR_FALSE;
|
|
|
|
if (-1 != linkTypes.IndexOf(NS_ConvertASCIItoUCS2("stylesheet"))) { // is it a stylesheet link?
|
|
|
|
if (-1 != linkTypes.IndexOf(NS_ConvertASCIItoUCS2("alternate"))) { // if alternate, does it have title?
|
|
if (0 == aTitle.Length()) { // alternates must have title
|
|
return NS_OK; //return without error, for now
|
|
} else {
|
|
isAlternate = PR_TRUE;
|
|
}
|
|
}
|
|
|
|
nsAutoString mimeType;
|
|
nsAutoString params;
|
|
SplitMimeType(aType, mimeType, params);
|
|
|
|
nsDTDMode mode;
|
|
mHTMLDocument->GetDTDMode(mode);
|
|
|
|
PRBool isStyleSheet = PR_FALSE; // see bug 18817
|
|
if (eDTDMode_strict== mode) {
|
|
if (mimeType.EqualsIgnoreCase("text/css")) {
|
|
isStyleSheet = PR_TRUE; // strict mode + good mime type
|
|
}
|
|
else {
|
|
if (0 == mimeType.Length()) {
|
|
nsString extension;
|
|
aHref.Right(extension, 4);
|
|
if (extension.EqualsWithConversion(".css")) {
|
|
isStyleSheet = PR_TRUE; // strict mode + no mime type + '.css' extension
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (0 == mimeType.Length() || mimeType.EqualsIgnoreCase("text/css")) {
|
|
isStyleSheet = PR_TRUE; // quirks mode + good mime type or no mime type at all
|
|
}
|
|
}
|
|
|
|
if (isStyleSheet) {
|
|
nsIURI* url = nsnull;
|
|
{
|
|
result = NS_NewURI(&url, aHref, mDocumentBaseURL);
|
|
}
|
|
if (NS_OK != result) {
|
|
return NS_OK; // The URL is bad, move along, don't propogate the error (for now)
|
|
}
|
|
|
|
if (-1 == linkTypes.IndexOf(NS_ConvertASCIItoUCS2("alternate"))) {
|
|
if (0 < aTitle.Length()) { // possibly preferred sheet
|
|
if (0 == mPreferredStyle.Length()) {
|
|
mPreferredStyle = aTitle;
|
|
mCSSLoader->SetPreferredSheet(aTitle);
|
|
mDocument->SetHeaderData(nsHTMLAtoms::headerDefaultStyle, aTitle);
|
|
}
|
|
}
|
|
}
|
|
|
|
PRBool blockParser = kBlockByDefault;
|
|
if (isAlternate) {
|
|
blockParser = PR_FALSE;
|
|
}
|
|
|
|
/* NOTE: no longer honoring the important keyword to indicate blocking
|
|
as it is proprietary and unnecessary since all non-alternate
|
|
will block the parser now -mja
|
|
if (-1 != linkTypes.IndexOf("important")) {
|
|
blockParser = PR_TRUE;
|
|
}
|
|
*/
|
|
|
|
PRBool doneLoading;
|
|
result = mCSSLoader->LoadStyleLink(aElement, url, aTitle, aMedia, kNameSpaceID_HTML,
|
|
mStyleSheetCount++,
|
|
((blockParser) ? mParser : nsnull),
|
|
doneLoading,
|
|
this);
|
|
NS_RELEASE(url);
|
|
if (NS_SUCCEEDED(result) && blockParser && (! doneLoading)) {
|
|
result = NS_ERROR_HTMLPARSER_BLOCK;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
nsresult
|
|
HTMLContentSink::ProcessLINKTag(const nsIParserNode& aNode)
|
|
{
|
|
nsresult result = NS_OK;
|
|
nsIHTMLContent* parent = nsnull;
|
|
|
|
if(mCurrentContext!=nsnull) {
|
|
parent=mCurrentContext->mStack[mCurrentContext->mStackPos-1].mContent;
|
|
}
|
|
|
|
if(parent!=nsnull) {
|
|
// Create content object
|
|
nsIHTMLContent* element = nsnull;
|
|
nsCOMPtr<nsINodeInfo> nodeInfo;
|
|
mNodeInfoManager->GetNodeInfo(nsHTMLAtoms::link, nsnull, kNameSpaceID_HTML,
|
|
*getter_AddRefs(nodeInfo));
|
|
|
|
result = NS_CreateHTMLElement(&element, nodeInfo);
|
|
if (NS_SUCCEEDED(result)) {
|
|
PRInt32 id;
|
|
mDocument->GetAndIncrementContentID(&id);
|
|
element->SetContentID(id);
|
|
|
|
// Add in the attributes and add the style content object to the
|
|
// head container.
|
|
element->SetDocument(mDocument, PR_FALSE, PR_TRUE);
|
|
result = AddAttributes(aNode, element);
|
|
if (NS_FAILED(result)) {
|
|
NS_RELEASE(element);
|
|
return result;
|
|
}
|
|
parent->AppendChildTo(element, PR_FALSE);
|
|
}
|
|
else {
|
|
return result;
|
|
}
|
|
|
|
// XXX need prefs. check here.
|
|
if(!mInsideNoXXXTag) {
|
|
PRInt32 i;
|
|
PRInt32 count = aNode.GetAttributeCount();
|
|
|
|
nsAutoString href;
|
|
nsAutoString rel;
|
|
nsAutoString title;
|
|
nsAutoString type;
|
|
nsAutoString media;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
nsAutoString key(aNode.GetKeyAt(i));
|
|
if (key.EqualsIgnoreCase("href")) {
|
|
GetAttributeValueAt(aNode, i, href);
|
|
href.StripWhitespace();
|
|
}
|
|
else if (key.EqualsIgnoreCase("rel")) {
|
|
GetAttributeValueAt(aNode, i, rel);
|
|
rel.CompressWhitespace();
|
|
}
|
|
else if (key.EqualsIgnoreCase("title")) {
|
|
GetAttributeValueAt(aNode, i, title);
|
|
title.CompressWhitespace();
|
|
}
|
|
else if (key.EqualsIgnoreCase("type")) {
|
|
GetAttributeValueAt(aNode, i, type);
|
|
type.StripWhitespace();
|
|
}
|
|
else if (key.EqualsIgnoreCase("media")) {
|
|
GetAttributeValueAt(aNode, i, media);
|
|
media.ToLowerCase(); // HTML4.0 spec is inconsistent, make it case INSENSITIVE
|
|
}
|
|
}
|
|
if (0 < href.Length()) {
|
|
result = ProcessStyleLink(element, href, rel, title, type, media);
|
|
}
|
|
}
|
|
NS_RELEASE(element);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
nsresult
|
|
HTMLContentSink::ProcessMAPTag(const nsIParserNode& aNode,
|
|
nsIHTMLContent* aContent)
|
|
{
|
|
nsresult rv;
|
|
|
|
NS_IF_RELEASE(mCurrentMap);
|
|
|
|
nsIDOMHTMLMapElement* domMap;
|
|
rv = aContent->QueryInterface(NS_GET_IID(nsIDOMHTMLMapElement), (void**)&domMap);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
// Strip out whitespace in the name for navigator compatability
|
|
// XXX NAV QUIRK -- XXX this should be done in the content node, not the sink
|
|
nsHTMLValue name;
|
|
aContent->GetHTMLAttribute(nsHTMLAtoms::name, name);
|
|
if (eHTMLUnit_String == name.GetUnit()) {
|
|
nsAutoString tmp;
|
|
name.GetStringValue(tmp);
|
|
tmp.StripWhitespace();
|
|
name.SetStringValue(tmp);
|
|
aContent->SetHTMLAttribute(nsHTMLAtoms::name, name, PR_FALSE);
|
|
}
|
|
|
|
// This is for nav4 compatibility. (Bug 18478)
|
|
// The base tag should only appear in the head,
|
|
// but nav4 allows the base tag in the body as well.
|
|
AddBaseTagInfo(aContent);
|
|
|
|
// Don't need to add the map to the document here anymore.
|
|
// The map adds itself
|
|
mCurrentMap = aContent;
|
|
NS_ADDREF(aContent);
|
|
NS_IF_RELEASE(domMap);
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
HTMLContentSink::ProcessMETATag(const nsIParserNode& aNode)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
nsIHTMLContent* parent = nsnull;
|
|
|
|
if(mCurrentContext!=nsnull) {
|
|
parent=mCurrentContext->mStack[mCurrentContext->mStackPos-1].mContent;
|
|
}
|
|
|
|
if (nsnull != parent) {
|
|
// Create content object
|
|
nsCOMPtr<nsINodeInfo> nodeInfo;
|
|
rv = mNodeInfoManager->GetNodeInfo(NS_ConvertASCIItoUCS2("meta"), nsnull,
|
|
kNameSpaceID_None,
|
|
*getter_AddRefs(nodeInfo));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsIHTMLContent* it;
|
|
rv = NS_NewHTMLMetaElement(&it, nodeInfo);
|
|
if (NS_OK == rv) {
|
|
// Add in the attributes and add the meta content object to the
|
|
// head container.
|
|
it->SetDocument(mDocument, PR_FALSE, PR_TRUE);
|
|
rv = AddAttributes(aNode, it);
|
|
if (NS_OK != rv) {
|
|
NS_RELEASE(it);
|
|
return rv;
|
|
}
|
|
parent->AppendChildTo(it, PR_FALSE);
|
|
|
|
// XXX It's just not sufficient to check if the parent is head. Also check for
|
|
// the preference.
|
|
if(!mInsideNoXXXTag) {
|
|
|
|
// set any HTTP-EQUIV data into document's header data as well as url
|
|
nsAutoString header;
|
|
it->GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::httpEquiv, header);
|
|
if (header.Length() > 0) {
|
|
nsAutoString result;
|
|
it->GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::content, result);
|
|
if (result.Length() > 0) {
|
|
// XXX necko isn't going to process headers coming in from the parser
|
|
//NS_WARNING("need to fix how necko adds mime headers (in HTMLContentSink::ProcessMETATag)");
|
|
|
|
nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(mWebShell, &rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
// see if we have a refresh "header".
|
|
if (!header.CompareWithConversion("refresh", PR_TRUE)) {
|
|
// Refresh headers are parsed with the following format in mind
|
|
// <META HTTP-EQUIV=REFRESH CONTENT="5; URL=http://uri">
|
|
// By the time we are here, the following is true:
|
|
// header = "REFRESH"
|
|
// result = "5; URL=http://uri" // note the URL attribute is
|
|
// optional, if it is absent, the currently loaded url is used.
|
|
// Also note that the second and URL seperator can be either a ';'
|
|
// or a ','. This breaks some websites. The ',' seperator should be
|
|
// illegal but CNN is using it.
|
|
|
|
//
|
|
// We need to handle the following strings.
|
|
// - X is a set of digits
|
|
// - URI is either a relative or absolute URI
|
|
// - FOO is any text
|
|
//
|
|
// ""
|
|
// empty string. use the currently loaded URI
|
|
// and refresh immediately.
|
|
// "X"
|
|
// Refresh the currently loaded URI in X milliseconds.
|
|
// "X; URI"
|
|
// Refresh using URI as the destination in X milliseconds.
|
|
// "X; URI; Blah"
|
|
// Refresh using URI as the destination in X milliseconds,
|
|
// ignoring "Blah"
|
|
// "URI"
|
|
// Refresh immediately using URI as the destination.
|
|
// "URI; Blah"
|
|
// Refresh immediately using URI as the destination,
|
|
// ignoring "Blah"
|
|
//
|
|
// Note that we need to remove any tokens wrapping the URI.
|
|
// These tokens currently include spaces, double and single
|
|
// quotes.
|
|
|
|
// first get our baseURI
|
|
nsCOMPtr<nsIURI> baseURI;
|
|
nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(docShell);
|
|
rv = webNav->GetCurrentURI(getter_AddRefs(baseURI));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
PRInt32 millis = -1;
|
|
nsAutoString uriAttrib;
|
|
|
|
PRInt32 semiColon = result.FindCharInSet(";,");
|
|
nsAutoString token;
|
|
if (semiColon > -1)
|
|
result.Left(token, semiColon);
|
|
else
|
|
token = result;
|
|
|
|
PRBool done = PR_FALSE;
|
|
while (!done && !token.IsEmpty()) {
|
|
token.CompressWhitespace();
|
|
// Ref. bug 22886
|
|
// Apparently CONTENT can also start with a period (.).
|
|
// Ex: <meta http-equiv = "refresh" content=".1; url=./recommendations1.html">
|
|
// So let's relax a little bit otherwise http://www.mozillazine.org/resources/
|
|
// wouldn't get redirected to the correct URL.
|
|
if (millis == -1 && (nsCRT::IsAsciiDigit(token.First()) || token.First()==PRUnichar('.'))) {
|
|
PRBool tokenIsANumber = PR_TRUE;
|
|
nsReadingIterator<PRUnichar> doneIterating; token.EndReading(doneIterating);
|
|
nsReadingIterator<PRUnichar> iter; token.BeginReading(iter);
|
|
while ( iter != doneIterating )
|
|
{
|
|
if (!(tokenIsANumber = nsCRT::IsAsciiDigit(*iter)) && *iter!=PRUnichar('.'))
|
|
break;
|
|
++iter;
|
|
}
|
|
|
|
if (tokenIsANumber) {
|
|
PRInt32 err;
|
|
millis = token.ToInteger(&err) * 1000;
|
|
} else {
|
|
done = PR_TRUE;
|
|
}
|
|
} else {
|
|
done = PR_TRUE;
|
|
}
|
|
if (done) {
|
|
PRInt32 loc = token.FindChar('=');
|
|
if (loc > -1)
|
|
token.Cut(0, loc+1);
|
|
token.Trim(" \"'");
|
|
uriAttrib.Assign(token);
|
|
} else {
|
|
// Increment to the next token.
|
|
if (semiColon > -1) {
|
|
semiColon++;
|
|
PRInt32 semiColon2 = result.FindCharInSet(";,", semiColon);
|
|
if (semiColon2 == -1) semiColon2 = result.Length();
|
|
result.Mid(token, semiColon, semiColon2 - semiColon);
|
|
semiColon = semiColon2;
|
|
} else {
|
|
done = PR_TRUE;
|
|
}
|
|
}
|
|
} // end while
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
if (uriAttrib.Length() == 0) {
|
|
uri = baseURI;
|
|
} else {
|
|
rv = NS_NewURI(getter_AddRefs(uri),
|
|
uriAttrib, baseURI);
|
|
}
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
NS_WITH_SERVICE(nsIScriptSecurityManager,
|
|
securityManager,
|
|
NS_SCRIPTSECURITYMANAGER_CONTRACTID,
|
|
&rv);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
rv = securityManager->CheckLoadURI(baseURI,
|
|
uri,
|
|
nsIScriptSecurityManager::DISALLOW_FROM_MAIL);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
nsCOMPtr<nsIRefreshURI> reefer =
|
|
do_QueryInterface(mWebShell);
|
|
if (reefer) {
|
|
if (millis == -1) millis = 0;
|
|
rv = reefer->RefreshURI(uri, millis,
|
|
PR_FALSE);
|
|
if (NS_FAILED(rv)) return rv;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // END refresh
|
|
else if (!header.CompareWithConversion("set-cookie", PR_TRUE)) {
|
|
nsCOMPtr<nsICookieService> cookieServ = do_GetService(NS_COOKIESERVICE_CONTRACTID, &rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
nsCOMPtr<nsIURI> baseURI;
|
|
nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(docShell);
|
|
rv = webNav->GetCurrentURI(getter_AddRefs(baseURI));
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
rv = cookieServ->SetCookieString(baseURI, mDocument, result);
|
|
if (NS_FAILED(rv)) return rv;
|
|
} // END set-cookie
|
|
|
|
header.ToLowerCase();
|
|
nsIAtom* fieldAtom = NS_NewAtom(header);
|
|
mDocument->SetHeaderData(fieldAtom, result);
|
|
|
|
if (fieldAtom == nsHTMLAtoms::headerDefaultStyle) {
|
|
mPreferredStyle = result;
|
|
mCSSLoader->SetPreferredSheet(mPreferredStyle);
|
|
}
|
|
else if (fieldAtom == nsHTMLAtoms::link) {
|
|
rv = ProcessLink(it, result);
|
|
}
|
|
else if (fieldAtom == nsHTMLAtoms::headerContentBase) {
|
|
ProcessBaseHref(result);
|
|
}
|
|
else if (fieldAtom == nsHTMLAtoms::headerWindowTarget) {
|
|
ProcessBaseTarget(result);
|
|
}
|
|
NS_IF_RELEASE(fieldAtom);
|
|
}//if (result.Length() > 0)
|
|
}//if (header.Length() > 0)
|
|
}//if(!mInsideNoXXXTag)
|
|
NS_RELEASE(it);
|
|
}//if (NS_OK == rv)
|
|
}//if (nsnull != parent)
|
|
|
|
return rv;
|
|
}
|
|
|
|
// Returns PR_TRUE if the language name is a version of JavaScript and
|
|
// PR_FALSE otherwise
|
|
static PRBool
|
|
IsJavaScriptLanguage(const nsString& aName, const char* *aVersion)
|
|
{
|
|
JSVersion version = JSVERSION_UNKNOWN;
|
|
|
|
if (aName.EqualsIgnoreCase("JavaScript") ||
|
|
aName.EqualsIgnoreCase("LiveScript") ||
|
|
aName.EqualsIgnoreCase("Mocha")) {
|
|
version = JSVERSION_DEFAULT;
|
|
}
|
|
else if (aName.EqualsIgnoreCase("JavaScript1.1")) {
|
|
version = JSVERSION_1_1;
|
|
}
|
|
else if (aName.EqualsIgnoreCase("JavaScript1.2")) {
|
|
version = JSVERSION_1_2;
|
|
}
|
|
else if (aName.EqualsIgnoreCase("JavaScript1.3")) {
|
|
version = JSVERSION_1_3;
|
|
}
|
|
else if (aName.EqualsIgnoreCase("JavaScript1.4")) {
|
|
version = JSVERSION_1_4;
|
|
}
|
|
else if (aName.EqualsIgnoreCase("JavaScript1.5")) {
|
|
version = JSVERSION_1_5;
|
|
}
|
|
if (version == JSVERSION_UNKNOWN)
|
|
return PR_FALSE;
|
|
*aVersion = JS_VersionToString(version);
|
|
return PR_TRUE;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void
|
|
HTMLContentSink::ForceReflow()
|
|
{
|
|
mCurrentContext->FlushTags(PR_TRUE);
|
|
}
|
|
#endif
|
|
|
|
|
|
void
|
|
HTMLContentSink::NotifyAppend(nsIContent* aContainer, PRInt32 aStartIndex)
|
|
{
|
|
mInNotification++;
|
|
MOZ_TIMER_DEBUGLOG(("Save and stop: nsHTMLContentSink::NotifyAppend()\n"));
|
|
MOZ_TIMER_SAVE(mWatch)
|
|
MOZ_TIMER_STOP(mWatch);
|
|
mDocument->ContentAppended(aContainer, aStartIndex);
|
|
mLastNotificationTime = PR_Now();
|
|
MOZ_TIMER_DEBUGLOG(("Restore: nsHTMLContentSink::NotifyAppend()\n"));
|
|
MOZ_TIMER_RESTORE(mWatch);
|
|
mInNotification--;
|
|
}
|
|
|
|
void
|
|
HTMLContentSink::NotifyInsert(nsIContent* aContent,
|
|
nsIContent* aChildContent,
|
|
PRInt32 aIndexInContainer)
|
|
{
|
|
mInNotification++;
|
|
MOZ_TIMER_DEBUGLOG(("Save and stop: nsHTMLContentSink::NotifyInsert()\n"));
|
|
MOZ_TIMER_SAVE(mWatch)
|
|
MOZ_TIMER_STOP(mWatch);
|
|
mDocument->ContentInserted(aContent, aChildContent, aIndexInContainer);
|
|
mLastNotificationTime = PR_Now();
|
|
MOZ_TIMER_DEBUGLOG(("Restore: nsHTMLContentSink::NotifyInsert()\n"));
|
|
MOZ_TIMER_RESTORE(mWatch);
|
|
mInNotification--;
|
|
}
|
|
|
|
PRBool
|
|
HTMLContentSink::IsMonolithicContainer(nsHTMLTag aTag)
|
|
{
|
|
if (eHTMLTag_tr == aTag || eHTMLTag_select == aTag) {
|
|
return PR_TRUE;
|
|
}
|
|
else {
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
|
|
PRBool
|
|
HTMLContentSink::IsTimeToNotify()
|
|
{
|
|
if (!mNotifyOnTimer || !mLayoutStarted || !mBackoffCount || mInMonolithicContainer) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRTime now = PR_Now();
|
|
PRInt64 interval, diff;
|
|
|
|
LL_I2L(interval, mNotificationInterval);
|
|
LL_SUB(diff, now, mLastNotificationTime);
|
|
|
|
if (LL_CMP(diff, >, interval)) {
|
|
mBackoffCount--;
|
|
return PR_TRUE;
|
|
}
|
|
else {
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
|
|
void
|
|
HTMLContentSink::UpdateAllContexts()
|
|
{
|
|
PRInt32 numContexts = mContextStack.Count();
|
|
for (PRInt32 i = 0; i < numContexts; i++) {
|
|
SinkContext* sc = (SinkContext*)mContextStack.ElementAt(i);
|
|
|
|
sc->UpdateChildCounts();
|
|
}
|
|
mCurrentContext->UpdateChildCounts();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::BeginUpdate(nsIDocument *aDocument)
|
|
{
|
|
nsresult result = NS_OK;
|
|
// If we're in a script and we didn't do the notification,
|
|
// something else in the script processing caused the
|
|
// notification to occur. Since this could result in frame
|
|
// creation, make sure we've flushed everything before we
|
|
// continue
|
|
if (mInScript && !mInNotification && mCurrentContext) {
|
|
result = mCurrentContext->FlushTags(PR_TRUE);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::EndUpdate(nsIDocument *aDocument)
|
|
{
|
|
|
|
// If we're in a script and we didn't do the notification,
|
|
// something else in the script processing caused the
|
|
// notification to occur. Update our notion of how much
|
|
// has been flushed to include any new content.
|
|
if (mInScript && !mInNotification) {
|
|
UpdateAllContexts();
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
HTMLContentSink::ResumeParsing()
|
|
{
|
|
nsresult result=NS_OK;
|
|
if (mParser) {
|
|
result=mParser->ContinueParsing();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
PRBool
|
|
HTMLContentSink::PreEvaluateScript()
|
|
{
|
|
// Eagerly append all pending elements (including the current body child)
|
|
// to the body (so that they can be seen by scripts) and force reflow.
|
|
SINK_TRACE(SINK_TRACE_CALLS,
|
|
("HTMLContentSink::PreEvaluateScript: flushing tags before evaluating script"));
|
|
mCurrentContext->FlushTags(PR_FALSE);
|
|
mCurrentContext->SetPreAppend(PR_TRUE);
|
|
|
|
mInScript++;
|
|
|
|
|
|
return PR_TRUE;
|
|
}
|
|
|
|
void
|
|
HTMLContentSink::PostEvaluateScript(PRBool aBodyPresent)
|
|
{
|
|
mInScript--;
|
|
mCurrentContext->SetPreAppend(PR_FALSE);
|
|
}
|
|
|
|
PRBool
|
|
HTMLContentSink::IsInScript()
|
|
{
|
|
return (mInScript > 0);
|
|
}
|
|
|
|
nsresult
|
|
HTMLContentSink::EvaluateScript(nsString& aScript,
|
|
nsIURI *aScriptURI,
|
|
PRInt32 aLineNo,
|
|
const char* aVersion)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
if (aScript.Length() > 0) {
|
|
nsCOMPtr<nsIScriptGlobalObject> globalObject;
|
|
mDocument->GetScriptGlobalObject(getter_AddRefs(globalObject));
|
|
NS_ENSURE_TRUE(globalObject, NS_ERROR_FAILURE);
|
|
|
|
nsCOMPtr<nsIScriptContext> context;
|
|
NS_ENSURE_SUCCESS(globalObject->GetContext(getter_AddRefs(context)),
|
|
NS_ERROR_FAILURE);
|
|
|
|
nsCOMPtr<nsIPrincipal> principal;
|
|
mDocument->GetPrincipal(getter_AddRefs(principal));
|
|
NS_ASSERTION(principal, "principal required for document");
|
|
|
|
nsAutoString ret;
|
|
char* url = nsnull;
|
|
|
|
if (aScriptURI) {
|
|
(void)aScriptURI->GetSpec(&url);
|
|
}
|
|
|
|
PRBool isUndefined;
|
|
context->EvaluateString(aScript, nsnull, principal, url,
|
|
aLineNo, aVersion, ret, &isUndefined);
|
|
|
|
if (url) {
|
|
nsCRT::free(url);
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::OnStreamComplete(nsIStreamLoader* aLoader,
|
|
nsISupports* aContext,
|
|
nsresult aStatus,
|
|
PRUint32 stringLen,
|
|
const char* string)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
if (stringLen) {
|
|
|
|
PRUnichar *unicodeString = nsnull;
|
|
PRInt32 unicodeLength = 0;
|
|
nsAutoString characterSet;
|
|
nsICharsetConverterManager *charsetConv = nsnull;
|
|
nsCOMPtr<nsIUnicodeDecoder> unicodeDecoder;
|
|
nsIAtom* contentTypeKey = NS_NewAtom("content-type");
|
|
nsXPIDLCString contenttypeheader;
|
|
nsCOMPtr<nsIHTTPChannel> httpChannel;
|
|
|
|
nsCOMPtr<nsIChannel> channel;
|
|
nsCOMPtr<nsIRequest> request;
|
|
rv = aLoader->GetRequest(getter_AddRefs(request));
|
|
NS_ASSERTION(request, "StreamLoader's request went away prematurely");
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
channel = do_QueryInterface(request);
|
|
|
|
if (channel) {
|
|
httpChannel = do_QueryInterface(channel);
|
|
if (httpChannel) {
|
|
rv = httpChannel->GetResponseHeader(contentTypeKey, getter_Copies(contenttypeheader));
|
|
NS_RELEASE(contentTypeKey);
|
|
}
|
|
}
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
nsAutoString contentType; contentType.AssignWithConversion( NS_STATIC_CAST(const char*, contenttypeheader) );
|
|
PRInt32 start = contentType.RFind("charset=", PR_TRUE ) ;
|
|
|
|
if(kNotFound != start)
|
|
{
|
|
start += 8; // 8 = "charset=".length
|
|
PRInt32 end = contentType.FindCharInSet(";\n\r ", start );
|
|
if(kNotFound == end ) end = contentType.Length();
|
|
|
|
contentType.Mid(characterSet, start, end - start);
|
|
NS_WITH_SERVICE(nsICharsetAlias, calias, kCharsetAliasCID, &rv);
|
|
if(NS_SUCCEEDED(rv) && (nsnull != calias) )
|
|
{
|
|
nsAutoString preferred;
|
|
rv = calias->GetPreferred(characterSet, preferred);
|
|
if(NS_SUCCEEDED(rv))
|
|
{
|
|
characterSet = preferred;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NS_FAILED(rv) || (characterSet.Length() == 0)) {
|
|
//charset from script charset tag
|
|
characterSet.Assign(mScriptCharset);
|
|
}
|
|
|
|
if (NS_FAILED(rv) || (characterSet.Length() == 0) ) {
|
|
// charset from document default
|
|
rv = mDocument->GetDocumentCharacterSet(characterSet);
|
|
}
|
|
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "Could not get document charset!");
|
|
|
|
rv = nsServiceManager::GetService(kCharsetConverterManagerCID,
|
|
NS_GET_IID(nsICharsetConverterManager),
|
|
(nsISupports**)&charsetConv);
|
|
|
|
if (NS_SUCCEEDED(rv) && (charsetConv))
|
|
{
|
|
rv = charsetConv->GetUnicodeDecoder(&characterSet,
|
|
getter_AddRefs(unicodeDecoder));
|
|
NS_RELEASE(charsetConv);
|
|
}
|
|
|
|
// converts from the charset to unicode
|
|
if (NS_SUCCEEDED(rv)) {
|
|
rv = unicodeDecoder->GetMaxLength(string, stringLen, &unicodeLength);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
mUnicodeXferBuf.SetCapacity(unicodeLength);
|
|
unicodeString = (PRUnichar *) mUnicodeXferBuf.GetUnicode();
|
|
rv = unicodeDecoder->Convert(string, (PRInt32 *) &stringLen, unicodeString, &unicodeLength);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
mUnicodeXferBuf.SetLength(unicodeLength);
|
|
} else {
|
|
mUnicodeXferBuf.SetLength(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "Could not convert external JavaScript to Unicode!");
|
|
|
|
if ((NS_OK == aStatus) && (NS_SUCCEEDED(rv))) {
|
|
|
|
nsAutoString jsUnicodeBuffer(CBufDescriptor(unicodeString, PR_TRUE, unicodeLength+1, unicodeLength));
|
|
PRBool bodyPresent = PreEvaluateScript();
|
|
|
|
//-- Merge the principal of the script file with that of the document
|
|
nsCOMPtr<nsISupports> owner;
|
|
channel->GetOwner(getter_AddRefs(owner));
|
|
nsCOMPtr<nsIPrincipal> prin;
|
|
if (owner)
|
|
{
|
|
prin = do_QueryInterface(owner, &rv);
|
|
if (NS_FAILED(rv)) return rv;
|
|
}
|
|
rv = mDocument->AddPrincipal(prin);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if(mParser) {
|
|
mParser->UnblockParser(); // make sure to unblock the parser before evaluating the script
|
|
}
|
|
|
|
rv = EvaluateScript(jsUnicodeBuffer, mScriptURI, 1, mScriptLanguageVersion);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
PostEvaluateScript(bodyPresent);
|
|
|
|
}
|
|
}
|
|
|
|
if(mParser && mParser->IsParserEnabled()){
|
|
rv=mParser->ContinueParsing();
|
|
}
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
//invalidate Xfer buffer content
|
|
mUnicodeXferBuf.SetLength(0);
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
HTMLContentSink::ProcessSCRIPTTag(const nsIParserNode& aNode)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
PRBool isJavaScript = PR_TRUE;
|
|
const char* jsVersionString = nsnull;
|
|
PRInt32 i, ac = aNode.GetAttributeCount();
|
|
|
|
// Look for SRC attribute and look for a LANGUAGE attribute
|
|
nsAutoString src;
|
|
for (i = 0; i < ac; i++) {
|
|
nsAutoString key(aNode.GetKeyAt(i));
|
|
if (key.EqualsIgnoreCase("src")) {
|
|
GetAttributeValueAt(aNode, i, src);
|
|
}
|
|
else if (key.EqualsIgnoreCase("type")) {
|
|
nsAutoString type;
|
|
|
|
GetAttributeValueAt(aNode, i, type);
|
|
|
|
nsAutoString mimeType;
|
|
nsAutoString params;
|
|
SplitMimeType(type, mimeType, params);
|
|
|
|
isJavaScript = mimeType.EqualsIgnoreCase("application/x-javascript") ||
|
|
mimeType.EqualsIgnoreCase("text/javascript");
|
|
if (isJavaScript) {
|
|
JSVersion jsVersion = JSVERSION_DEFAULT;
|
|
if (params.Find("version=", PR_TRUE) == 0) {
|
|
if (params.Length() != 11 || params[8] != '1' || params[9] != '.')
|
|
jsVersion = JSVERSION_UNKNOWN;
|
|
else switch (params[10]) {
|
|
case '0': jsVersion = JSVERSION_1_0; break;
|
|
case '1': jsVersion = JSVERSION_1_1; break;
|
|
case '2': jsVersion = JSVERSION_1_2; break;
|
|
case '3': jsVersion = JSVERSION_1_3; break;
|
|
case '4': jsVersion = JSVERSION_1_4; break;
|
|
case '5': jsVersion = JSVERSION_1_5; break;
|
|
default: jsVersion = JSVERSION_UNKNOWN;
|
|
}
|
|
}
|
|
jsVersionString = JS_VersionToString(jsVersion);
|
|
}
|
|
}
|
|
else if (key.EqualsIgnoreCase("language")) {
|
|
nsAutoString lang;
|
|
|
|
GetAttributeValueAt(aNode, i, lang);
|
|
isJavaScript = IsJavaScriptLanguage(lang, &jsVersionString);
|
|
}
|
|
else if (key.EqualsIgnoreCase("charset")) {
|
|
//charset from script charset tag
|
|
nsAutoString charset;
|
|
|
|
GetAttributeValueAt(aNode, i, charset);
|
|
NS_WITH_SERVICE(nsICharsetAlias, calias, kCharsetAliasCID, &rv);
|
|
if(NS_SUCCEEDED(rv) && (nsnull != calias) )
|
|
{
|
|
rv = calias->GetPreferred(charset, mScriptCharset);
|
|
} else {
|
|
mScriptCharset = charset;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create content object
|
|
NS_ASSERTION(mCurrentContext->mStackPos > 0, "leaf w/o container");
|
|
if (mCurrentContext->mStackPos <= 0) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
nsIHTMLContent* parent = mCurrentContext->mStack[mCurrentContext->mStackPos-1].mContent;
|
|
nsIHTMLContent* element = nsnull;
|
|
nsCOMPtr<nsINodeInfo> nodeInfo;
|
|
mNodeInfoManager->GetNodeInfo(nsHTMLAtoms::script, nsnull, kNameSpaceID_None,
|
|
*getter_AddRefs(nodeInfo));
|
|
|
|
rv = NS_CreateHTMLElement(&element, nodeInfo);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
PRInt32 id;
|
|
mDocument->GetAndIncrementContentID(&id);
|
|
element->SetContentID(id);
|
|
|
|
// Add in the attributes and add the style content object to the
|
|
// head container.
|
|
element->SetDocument(mDocument, PR_FALSE, PR_TRUE);
|
|
rv = AddAttributes(aNode, element);
|
|
if (NS_FAILED(rv)) {
|
|
NS_RELEASE(element);
|
|
return rv;
|
|
}
|
|
if (mCurrentContext->mStack[mCurrentContext->mStackPos-1].mInsertionPoint != -1) {
|
|
parent->InsertChildAt(element,
|
|
mCurrentContext->mStack[mCurrentContext->mStackPos-1].mInsertionPoint++,
|
|
PR_FALSE);
|
|
}
|
|
else {
|
|
parent->AppendChildTo(element, PR_FALSE);
|
|
}
|
|
}
|
|
else {
|
|
return rv;
|
|
}
|
|
|
|
// Create a text node holding the content
|
|
// First, get the text content of the script tag
|
|
nsAutoString script;
|
|
script.Assign(aNode.GetSkippedContent());
|
|
|
|
if (script.Length() > 0) {
|
|
nsIContent* text;
|
|
rv = NS_NewTextNode(&text);
|
|
if (NS_OK == rv) {
|
|
nsIDOMText* tc;
|
|
rv = text->QueryInterface(NS_GET_IID(nsIDOMText), (void**)&tc);
|
|
if (NS_OK == rv) {
|
|
tc->SetData(script);
|
|
NS_RELEASE(tc);
|
|
}
|
|
element->AppendChildTo(text, PR_FALSE);
|
|
text->SetDocument(mDocument, PR_FALSE, PR_TRUE);
|
|
NS_RELEASE(text);
|
|
}
|
|
}
|
|
NS_RELEASE(element);
|
|
|
|
// Don't include script loading and evaluation in the stopwatch
|
|
// that is measuring content creation time
|
|
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::ProcessSCRIPTTag()\n"));
|
|
MOZ_TIMER_STOP(mWatch);
|
|
|
|
// Don't process scripts that aren't JavaScript and don't process
|
|
// scripts that are inside iframes, noframe, or noscript tags,
|
|
// or if the script context has script evaluation disabled:
|
|
PRBool scriptsEnabled = PR_TRUE;
|
|
nsCOMPtr<nsIScriptGlobalObject> globalObject;
|
|
mDocument->GetScriptGlobalObject(getter_AddRefs(globalObject));
|
|
if (globalObject)
|
|
{
|
|
nsCOMPtr<nsIScriptContext> context;
|
|
if (NS_SUCCEEDED(globalObject->GetContext(getter_AddRefs(context)))
|
|
&& context)
|
|
context->GetScriptsEnabled(&scriptsEnabled);
|
|
}
|
|
|
|
if (scriptsEnabled && isJavaScript && !mNumOpenIFRAMES && !mInsideNoXXXTag) {
|
|
mScriptLanguageVersion = jsVersionString;
|
|
|
|
// If there is a SRC attribute...
|
|
if (src.Length() > 0) {
|
|
// Use the SRC attribute value to load the URL
|
|
{
|
|
rv = NS_NewURI(getter_AddRefs(mScriptURI), src, mDocumentBaseURL);
|
|
}
|
|
if (NS_OK != rv) {
|
|
return rv;
|
|
}
|
|
|
|
// Check that this page is allowed to load this URI.
|
|
NS_WITH_SERVICE(nsIScriptSecurityManager, securityManager,
|
|
NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
rv = securityManager->CheckLoadURI(mDocumentBaseURL, mScriptURI, nsIScriptSecurityManager::ALLOW_CHROME);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
// After the security manager, the content-policy stuff gets a veto
|
|
// For pinkerton: a symphony for string conversion, in 3 parts.
|
|
nsXPIDLCString urlCString;
|
|
mScriptURI->GetSpec(getter_Copies(urlCString));
|
|
nsAutoString url;
|
|
url.AssignWithConversion((const char *)urlCString);
|
|
nsCOMPtr<nsIDOMElement> DOMElement = do_QueryInterface(element, &rv);
|
|
|
|
PRBool shouldLoad = PR_TRUE;
|
|
if (NS_SUCCEEDED(rv) &&
|
|
(rv = NS_CheckContentLoadPolicy(nsIContentPolicy::CONTENT_SCRIPT,
|
|
url, DOMElement, &shouldLoad),
|
|
NS_SUCCEEDED(rv)) &&
|
|
!shouldLoad) {
|
|
|
|
// content-policy veto causes silent failure
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsILoadGroup> loadGroup;
|
|
nsCOMPtr<nsIStreamLoader> loader;
|
|
|
|
mDocument->GetDocumentLoadGroup(getter_AddRefs(loadGroup));
|
|
|
|
// supply a prompter if you have one. this allows modal dialogs put up
|
|
// from within this new stream loader to have proper parenting. but it's
|
|
// not fatal if there isn't a prompter.
|
|
nsCOMPtr<nsIInterfaceRequestor> promptcall(do_QueryInterface(mWebShell));
|
|
rv = NS_NewStreamLoader(getter_AddRefs(loader), mScriptURI, this,
|
|
nsnull, loadGroup, promptcall,
|
|
nsIChannel::FORCE_RELOAD);
|
|
if (NS_OK == rv) {
|
|
rv = NS_ERROR_HTMLPARSER_BLOCK;
|
|
}
|
|
}
|
|
else {
|
|
PRBool bodyPresent = PreEvaluateScript();
|
|
|
|
PRUint32 lineNo = (PRUint32)aNode.GetSourceLineNumber();
|
|
nsIURI *docURI = mDocument->GetDocumentURL();
|
|
|
|
EvaluateScript(script, docURI, lineNo, jsVersionString);
|
|
NS_IF_RELEASE(docURI);
|
|
|
|
PostEvaluateScript(bodyPresent);
|
|
|
|
// If the parse was disabled as a result of this evaluate script
|
|
// (for example, if the script document.wrote a SCRIPT SRC= tag,
|
|
// we remind the parser to block.
|
|
if ((nsnull != mParser) && (PR_FALSE == mParser->IsParserEnabled())) {
|
|
rv = NS_ERROR_HTMLPARSER_BLOCK;
|
|
}
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
// 3 ways to load a style sheet: inline, style src=, link tag
|
|
// XXX What does nav do if we have SRC= and some style data inline?
|
|
|
|
nsresult
|
|
HTMLContentSink::ProcessSTYLETag(const nsIParserNode& aNode)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
nsIHTMLContent* parent = nsnull;
|
|
|
|
if(mCurrentContext!=nsnull) {
|
|
parent=mCurrentContext->mStack[mCurrentContext->mStackPos-1].mContent;
|
|
}
|
|
|
|
if(parent!=nsnull) {
|
|
// Create content object
|
|
nsIHTMLContent* element = nsnull;
|
|
nsCOMPtr<nsINodeInfo> nodeInfo;
|
|
mNodeInfoManager->GetNodeInfo(nsHTMLAtoms::style, nsnull,
|
|
kNameSpaceID_None,
|
|
*getter_AddRefs(nodeInfo));
|
|
|
|
rv = NS_CreateHTMLElement(&element, nodeInfo);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
PRInt32 id;
|
|
mDocument->GetAndIncrementContentID(&id);
|
|
element->SetContentID(id);
|
|
|
|
// Add in the attributes and add the style content object to the
|
|
// head container.
|
|
element->SetDocument(mDocument, PR_FALSE, PR_TRUE);
|
|
rv = AddAttributes(aNode, element);
|
|
if (NS_FAILED(rv)) {
|
|
NS_RELEASE(element);
|
|
return rv;
|
|
}
|
|
parent->AppendChildTo(element, PR_FALSE);
|
|
}
|
|
|
|
if(!mInsideNoXXXTag && NS_SUCCEEDED(rv)) {
|
|
|
|
PRInt32 i, count = aNode.GetAttributeCount();
|
|
|
|
nsAutoString src;
|
|
nsAutoString title;
|
|
nsAutoString type;
|
|
nsAutoString media;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
nsAutoString key(aNode.GetKeyAt(i));
|
|
if (key.EqualsIgnoreCase("src")) {
|
|
GetAttributeValueAt(aNode, i, src);
|
|
src.StripWhitespace();
|
|
}
|
|
else if (key.EqualsIgnoreCase("title")) {
|
|
GetAttributeValueAt(aNode, i, title);
|
|
title.CompressWhitespace();
|
|
}
|
|
else if (key.EqualsIgnoreCase("type")) {
|
|
GetAttributeValueAt(aNode, i, type);
|
|
type.StripWhitespace();
|
|
}
|
|
else if (key.EqualsIgnoreCase("media")) {
|
|
GetAttributeValueAt(aNode, i, media);
|
|
media.ToLowerCase(); // HTML4.0 spec is inconsistent, make it case INSENSITIVE
|
|
}
|
|
}
|
|
|
|
|
|
nsAutoString mimeType;
|
|
nsAutoString params;
|
|
SplitMimeType(type, mimeType, params);
|
|
|
|
PRBool blockParser = kBlockByDefault;
|
|
|
|
if ((0 == mimeType.Length()) || mimeType.EqualsIgnoreCase("text/css")) {
|
|
|
|
if (0 < title.Length()) { // possibly preferred sheet
|
|
if (0 == mPreferredStyle.Length()) {
|
|
mPreferredStyle = title;
|
|
mCSSLoader->SetPreferredSheet(title);
|
|
mDocument->SetHeaderData(nsHTMLAtoms::headerDefaultStyle, title);
|
|
}
|
|
}
|
|
|
|
// The skipped content contains the inline style data
|
|
const nsString& content = aNode.GetSkippedContent();
|
|
PRBool doneLoading = PR_FALSE;
|
|
|
|
nsIUnicharInputStream* uin = nsnull;
|
|
if (0 == src.Length()) {
|
|
|
|
// Create a text node holding the content
|
|
nsIContent* text;
|
|
rv = NS_NewTextNode(&text);
|
|
if (NS_OK == rv) {
|
|
nsIDOMText* tc;
|
|
rv = text->QueryInterface(NS_GET_IID(nsIDOMText), (void**)&tc);
|
|
if (NS_OK == rv) {
|
|
tc->SetData(content);
|
|
NS_RELEASE(tc);
|
|
}
|
|
element->AppendChildTo(text, PR_FALSE);
|
|
text->SetDocument(mDocument, PR_FALSE, PR_TRUE);
|
|
NS_RELEASE(text);
|
|
}
|
|
|
|
// Create a string to hold the data and wrap it up in a unicode
|
|
// input stream.
|
|
rv = NS_NewStringUnicharInputStream(&uin, new nsString(content));
|
|
if (NS_OK != rv) {
|
|
return rv;
|
|
}
|
|
|
|
// Now that we have a url and a unicode input stream, parse the
|
|
// style sheet.
|
|
rv = mCSSLoader->LoadInlineStyle(element, uin, title, media, kNameSpaceID_HTML,
|
|
mStyleSheetCount++,
|
|
((blockParser) ? mParser : nsnull),
|
|
doneLoading, this);
|
|
NS_RELEASE(uin);
|
|
}
|
|
else {
|
|
// src with immediate style data doesn't add up
|
|
// XXX what does nav do?
|
|
// Use the SRC attribute value to load the URL
|
|
nsIURI* url = nsnull;
|
|
{
|
|
rv = NS_NewURI(&url, src, mDocumentBaseURL);
|
|
}
|
|
if (NS_OK != rv) {
|
|
return rv;
|
|
}
|
|
|
|
rv = mCSSLoader->LoadStyleLink(element, url, title, media, kNameSpaceID_HTML,
|
|
mStyleSheetCount++,
|
|
((blockParser) ? mParser : nsnull),
|
|
doneLoading, this);
|
|
NS_RELEASE(url);
|
|
}
|
|
if (NS_SUCCEEDED(rv) && blockParser && (! doneLoading)) {
|
|
rv = NS_ERROR_HTMLPARSER_BLOCK;
|
|
}
|
|
}//if ((0 == mimeType.Length()) || mimeType.EqualsIgnoreCase("text/css"))
|
|
}//if(!mInsideNoXXXTag && NS_SUCCEEDED(rv))
|
|
NS_RELEASE(element);
|
|
}//if(parent!=nsnull)
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::NotifyError(const nsParserError* aError)
|
|
{
|
|
// Errors in HTML? Who would have thought!
|
|
// Why are you telling us, parser. Deal with it yourself.
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::FlushPendingNotifications()
|
|
{
|
|
nsresult result = NS_OK;
|
|
// Only flush tags if we're not doing the notification ourselves
|
|
// (since we aren't reentrant) and if we're in a script (since
|
|
// we only care to flush if this is done via script).
|
|
// Bug 4891: Also flush outside of <script> tags - if script is executing
|
|
// after the page has finished loading, and a document.write
|
|
// occurs, we might need this flush.
|
|
if (mCurrentContext && !mInNotification) {
|
|
result = mCurrentContext->FlushTags();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::DoFragment(PRBool aFlag)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
/**
|
|
* This will dump content model into the output file.
|
|
*
|
|
* @update harishd 05/25/00
|
|
* @param
|
|
* @return NS_OK all went well else ERROR
|
|
*/
|
|
|
|
NS_IMETHODIMP
|
|
HTMLContentSink::DumpContentModel()
|
|
{
|
|
nsresult result=NS_OK;
|
|
FILE* out=::fopen("rtest_html.txt", "a");
|
|
if(out!=nsnull) {
|
|
if(mDocument) {
|
|
nsIContent* root = mDocument->GetRootContent();
|
|
if(root) {
|
|
if(mDocumentURI) {
|
|
char* buff[1]={0};
|
|
mDocumentURI->GetSpec(buff);
|
|
fputs(buff[0],out);
|
|
}
|
|
fputs(";",out);
|
|
result=root->DumpContent(out,0,PR_FALSE);
|
|
fputs(";\n",out);
|
|
NS_RELEASE(root);
|
|
}
|
|
}
|
|
fclose(out);
|
|
}
|
|
return result;
|
|
}
|