Back to index

lightning-sunbird  0.9+nobinonly
nsHTMLContentSink.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *   Peter Annema <disttsc@bart.nl>
00025  *   Daniel Glazman <glazman@netscape.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nsContentSink.h"
00042 #include "nsCOMPtr.h"
00043 #include "nsReadableUtils.h"
00044 #include "nsUnicharUtils.h"
00045 #include "nsIHTMLContentSink.h"
00046 #include "nsIInterfaceRequestor.h"
00047 #include "nsIInterfaceRequestorUtils.h"
00048 #include "nsIParser.h"
00049 #include "nsParserUtils.h"
00050 #include "nsIScriptLoader.h"
00051 #include "nsIURI.h"
00052 #include "nsNetUtil.h"
00053 #include "nsIPresShell.h"
00054 #include "nsIViewManager.h"
00055 #include "nsIWidget.h"
00056 #include "nsIContentViewer.h"
00057 #include "nsIMarkupDocumentViewer.h"
00058 #include "nsINodeInfo.h"
00059 #include "nsHTMLTokens.h"
00060 #include "nsCRT.h"
00061 #include "prtime.h"
00062 #include "prlog.h"
00063 #include "nsInt64.h"
00064 
00065 #include "nsGenericHTMLElement.h"
00066 #include "nsITextContent.h"
00067 
00068 #include "nsIDOMText.h"
00069 #include "nsIDOMComment.h"
00070 #include "nsIDOMDocument.h"
00071 #include "nsIDOMNSDocument.h"
00072 #include "nsIDOMDOMImplementation.h"
00073 #include "nsIDOMDocumentType.h"
00074 #include "nsIDOMHTMLScriptElement.h"
00075 #include "nsIScriptElement.h"
00076 
00077 #include "nsIDOMHTMLFormElement.h"
00078 #include "nsIDOMHTMLTextAreaElement.h"
00079 #include "nsIFormControl.h"
00080 #include "nsIForm.h"
00081 
00082 #include "nsIComponentManager.h"
00083 #include "nsIServiceManager.h"
00084 
00085 #include "nsHTMLAtoms.h"
00086 #include "nsContentUtils.h"
00087 #include "nsIFrame.h"
00088 #include "nsIChannel.h"
00089 #include "nsIHttpChannel.h"
00090 #include "nsIDocShell.h"
00091 #include "nsIDocument.h"
00092 #include "nsStubDocumentObserver.h"
00093 #include "nsIHTMLDocument.h"
00094 #include "nsINameSpaceManager.h"
00095 #include "nsIDOMHTMLMapElement.h"
00096 #include "nsICookieService.h"
00097 #include "nsVoidArray.h"
00098 #include "nsIScriptSecurityManager.h"
00099 #include "nsIPrincipal.h"
00100 #include "nsTextFragment.h"
00101 #include "nsIScriptGlobalObject.h"
00102 #include "nsIScriptGlobalObjectOwner.h"
00103 
00104 #include "nsIParserService.h"
00105 #include "nsISelectElement.h"
00106 
00107 #include "nsIStyleSheetLinkingElement.h"
00108 #include "nsTimer.h"
00109 #include "nsITimer.h"
00110 #include "nsDOMError.h"
00111 #include "nsContentPolicyUtils.h"
00112 #include "nsIScriptContext.h"
00113 #include "nsStyleLinkElement.h"
00114 
00115 #include "nsReadableUtils.h"
00116 #include "nsWeakReference.h" // nsHTMLElementFactory supports weak references
00117 #include "nsIPrompt.h"
00118 #include "nsLayoutCID.h"
00119 #include "nsIDocShellTreeItem.h"
00120 #include "plevent.h"
00121 
00122 #include "nsEscape.h"
00123 #include "nsIElementObserver.h"
00124 #include "nsNodeInfoManager.h"
00125 #include "nsContentCreatorFunctions.h"
00126 
00127 //----------------------------------------------------------------------
00128 
00129 #ifdef NS_DEBUG
00130 static PRLogModuleInfo* gSinkLogModuleInfo;
00131 
00132 #define SINK_TRACE_CALLS              0x1
00133 #define SINK_TRACE_REFLOW             0x2
00134 #define SINK_ALWAYS_REFLOW            0x4
00135 
00136 #define SINK_LOG_TEST(_lm, _bit) (PRIntn((_lm)->level) & (_bit))
00137 
00138 #define SINK_TRACE(_bit, _args)                       \
00139   PR_BEGIN_MACRO                                      \
00140     if (SINK_LOG_TEST(gSinkLogModuleInfo, _bit)) {    \
00141       PR_LogPrint _args;                              \
00142     }                                                 \
00143   PR_END_MACRO
00144 
00145 #define SINK_TRACE_NODE(_bit, _msg, _tag, _sp, _obj) \
00146   _obj->SinkTraceNode(_bit, _msg, _tag, _sp, this)
00147 
00148 #else
00149 #define SINK_TRACE(_bit, _args)
00150 #define SINK_TRACE_NODE(_bit, _msg, _tag, _sp, _obj)
00151 #endif
00152 
00153 #undef SINK_NO_INCREMENTAL
00154 
00155 //----------------------------------------------------------------------
00156 
00157 #define NS_SINK_FLAG_SCRIPT_ENABLED       0x00000008
00158 
00159 #define NS_SINK_FLAG_FRAMES_ENABLED       0x00000010
00160 
00161 // Interrupt parsing when mMaxTokenProcessingTime is exceeded
00162 #define NS_SINK_FLAG_CAN_INTERRUPT_PARSER 0x00000020
00163 
00164 // Lower the value for mNotificationInterval and
00165 // mMaxTokenProcessingTime
00166 #define NS_SINK_FLAG_DYNAMIC_LOWER_VALUE  0x00000040
00167 
00168 #define NS_SINK_FLAG_FORM_ON_STACK        0x00000080
00169 
00170 #define NS_SINK_FLAG_PARSING              0x00000100
00171 
00172 #define NS_SINK_FLAG_DROPPED_TIMER        0x00000200
00173 
00174 // 1/2 second fudge factor for window creation
00175 #define NS_DELAY_FOR_WINDOW_CREATION  500000
00176 
00177 // 200 determined empirically to provide good user response without
00178 // sampling the clock too often.
00179 #define NS_MAX_TOKENS_DEFLECTED_IN_LOW_FREQ_MODE 200
00180 
00181 typedef nsGenericHTMLElement* (*contentCreatorCallback)(nsINodeInfo*, PRBool aFromParser);
00182 
00183 nsGenericHTMLElement*
00184 NS_NewHTMLNOTUSEDElement(nsINodeInfo *aNodeInfo, PRBool aFromParser)
00185 {
00186   NS_NOTREACHED("The element ctor should never be called");
00187   return nsnull;
00188 }
00189 
00190 #define HTML_TAG(_tag, _classname) NS_NewHTML##_classname##Element,
00191 #define HTML_OTHER(_tag) NS_NewHTMLNOTUSEDElement,
00192 static const contentCreatorCallback sContentCreatorCallbacks[] = {
00193   NS_NewHTMLUnknownElement,
00194 #include "nsHTMLTagList.h"
00195 #undef HTML_TAG
00196 #undef HTML_OTHER
00197   NS_NewHTMLUnknownElement
00198 };
00199 
00200 class SinkContext;
00201 
00202 class HTMLContentSink : public nsContentSink,
00203                         public nsIHTMLContentSink,
00204                         public nsITimerCallback,
00205 #ifdef DEBUG
00206                         public nsIDebugDumpContent,
00207 #endif
00208                         public nsStubDocumentObserver
00209 {
00210 public:
00211   friend class SinkContext;
00212   friend class DummyParserRequest;
00213 
00214   HTMLContentSink();
00215   virtual ~HTMLContentSink();
00216 
00217   NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
00218 
00219   nsresult Init(nsIDocument* aDoc, nsIURI* aURI, nsISupports* aContainer,
00220                 nsIChannel* aChannel);
00221 
00222   // nsISupports
00223   NS_DECL_ISUPPORTS_INHERITED
00224 
00225   // nsIContentSink
00226   NS_IMETHOD WillBuildModel(void);
00227   NS_IMETHOD DidBuildModel(void);
00228   NS_IMETHOD WillInterrupt(void);
00229   NS_IMETHOD WillResume(void);
00230   NS_IMETHOD SetParser(nsIParser* aParser);
00231   virtual void FlushPendingNotifications(mozFlushType aType);
00232   NS_IMETHOD SetDocumentCharset(nsACString& aCharset);
00233   virtual nsISupports *GetTarget();
00234 
00235   // nsIHTMLContentSink
00236   NS_IMETHOD OpenContainer(const nsIParserNode& aNode);
00237   NS_IMETHOD CloseContainer(const nsHTMLTag aTag);
00238   NS_IMETHOD AddHeadContent(const nsIParserNode& aNode);
00239   NS_IMETHOD AddLeaf(const nsIParserNode& aNode);
00240   NS_IMETHOD AddComment(const nsIParserNode& aNode);
00241   NS_IMETHOD AddProcessingInstruction(const nsIParserNode& aNode);
00242   NS_IMETHOD AddDocTypeDecl(const nsIParserNode& aNode);
00243   NS_IMETHOD WillProcessTokens(void);
00244   NS_IMETHOD DidProcessTokens(void);
00245   NS_IMETHOD WillProcessAToken(void);
00246   NS_IMETHOD DidProcessAToken(void);
00247   NS_IMETHOD NotifyTagObservers(nsIParserNode* aNode);
00248   NS_IMETHOD BeginContext(PRInt32 aID);
00249   NS_IMETHOD EndContext(PRInt32 aID);
00250   NS_IMETHOD SetTitle(const nsString& aValue);
00251   NS_IMETHOD OpenHTML(const nsIParserNode& aNode);
00252   NS_IMETHOD CloseHTML();
00253   NS_IMETHOD OpenHead(const nsIParserNode& aNode);
00254   NS_IMETHOD CloseHead();
00255   NS_IMETHOD OpenBody(const nsIParserNode& aNode);
00256   NS_IMETHOD CloseBody();
00257   NS_IMETHOD OpenForm(const nsIParserNode& aNode);
00258   NS_IMETHOD CloseForm();
00259   NS_IMETHOD OpenFrameset(const nsIParserNode& aNode);
00260   NS_IMETHOD CloseFrameset();
00261   NS_IMETHOD OpenMap(const nsIParserNode& aNode);
00262   NS_IMETHOD CloseMap();
00263   NS_IMETHOD IsEnabled(PRInt32 aTag, PRBool* aReturn);
00264   NS_IMETHOD_(PRBool) IsFormOnStack();
00265 
00266   // nsITimerCallback
00267   NS_DECL_NSITIMERCALLBACK
00268   
00269   // nsIDocumentObserver
00270   virtual void BeginUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType);
00271   virtual void EndUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType);
00272 
00273 #ifdef DEBUG
00274   // nsIDebugDumpContent
00275   NS_IMETHOD DumpContentModel();
00276 #endif
00277 
00278 protected:
00279   PRBool IsTimeToNotify();
00280 
00281   nsresult SetDocumentTitle(const nsAString& aTitle, const nsIParserNode* aNode);
00282   // If aCheckIfPresent is true, will only set an attribute in cases
00283   // when it's not already set.
00284   nsresult AddAttributes(const nsIParserNode& aNode, nsIContent* aContent,
00285                          PRBool aNotify = PR_FALSE,
00286                          PRBool aCheckIfPresent = PR_FALSE);
00287   already_AddRefed<nsGenericHTMLElement>
00288   CreateContentObject(const nsIParserNode& aNode, nsHTMLTag aNodeType,
00289                       nsGenericHTMLElement* aForm,
00290                       nsIDocShell* aDocShell);
00291 
00292   inline PRInt32 GetNotificationInterval()
00293   {
00294     if (mFlags & NS_SINK_FLAG_DYNAMIC_LOWER_VALUE) {
00295       return 1000;
00296     }
00297 
00298     return mNotificationInterval;
00299   }
00300 
00301   inline PRInt32 GetMaxTokenProcessingTime()
00302   {
00303     if (mFlags & NS_SINK_FLAG_DYNAMIC_LOWER_VALUE) {
00304       return 3000;
00305     }
00306 
00307     return mMaxTokenProcessingTime;
00308   }
00309 
00310 #ifdef NS_DEBUG
00311   void SinkTraceNode(PRUint32 aBit,
00312                      const char* aMsg,
00313                      const nsHTMLTag aTag,
00314                      PRInt32 aStackPos,
00315                      void* aThis);
00316 #endif
00317 
00318   nsIHTMLDocument* mHTMLDocument;
00319 
00320   // back off timer notification after count
00321   PRInt32 mBackoffCount;
00322 
00323   // Notification interval in microseconds
00324   PRInt32 mNotificationInterval;
00325 
00326   // Time of last notification
00327   PRTime mLastNotificationTime;
00328 
00329   // Timer used for notification
00330   nsCOMPtr<nsITimer> mNotificationTimer;
00331 
00332   // The maximum length of a text run
00333   PRInt32 mMaxTextRun;
00334 
00335   nsGenericHTMLElement* mRoot;
00336   nsGenericHTMLElement* mBody;
00337   nsGenericHTMLElement* mFrameset;
00338   nsGenericHTMLElement* mHead;
00339 
00340   nsString mSkippedContent;
00341 
00342   // Do we notify based on time?
00343   PRPackedBool mNotifyOnTimer;
00344 
00345   PRPackedBool mLayoutStarted;
00346   PRPackedBool mScrolledToRefAlready;
00347 
00348   PRInt32 mInNotification;
00349   nsRefPtr<nsGenericHTMLElement> mCurrentForm;
00350   nsCOMPtr<nsIContent> mCurrentMap;
00351 
00352   nsAutoVoidArray mContextStack;
00353   SinkContext* mCurrentContext;
00354   SinkContext* mHeadContext;
00355   PRInt32 mNumOpenIFRAMES;
00356   nsCOMPtr<nsIRequest> mDummyParserRequest;
00357 
00358   nsString mBaseHREF;
00359   nsString mBaseTarget;
00360 
00361   // depth of containment within <noembed>, <noframes> etc
00362   PRInt32 mInsideNoXXXTag;
00363   PRInt32 mInMonolithicContainer;
00364   PRUint32 mFlags;
00365 
00366   // -- Can interrupt parsing members --
00367   PRUint32 mDelayTimerStart;
00368 
00369   // Interrupt parsing during token procesing after # of microseconds
00370   PRInt32 mMaxTokenProcessingTime;
00371 
00372   // Switch between intervals when time is exceeded
00373   PRInt32 mDynamicIntervalSwitchThreshold;
00374 
00375   PRInt32 mBeginLoadTime;
00376 
00377   // Last mouse event or keyboard event time sampled by the content
00378   // sink
00379   PRUint32 mLastSampledUserEventTime;
00380 
00381   // The number of tokens that have been processed while in the low
00382   // frequency parser interrupt mode without falling through to the
00383   // logic which decides whether to switch to the high frequency
00384   // parser interrupt mode.
00385   PRUint8 mDeflectedCount;
00386 
00387   nsCOMPtr<nsIObserverEntry> mObservers;
00388 
00389   void StartLayout();
00390 
00391   void TryToScrollToRef();
00392 
00399   void AddBaseTagInfo(nsIContent* aContent);
00400 
00401   void ProcessBaseHref(const nsAString& aBaseHref);
00402   void ProcessBaseTarget(const nsAString& aBaseTarget);
00403 
00404   // Routines for tags that require special handling
00405   nsresult ProcessAREATag(const nsIParserNode& aNode);
00406   nsresult ProcessBASETag(const nsIParserNode& aNode);
00407   nsresult ProcessLINKTag(const nsIParserNode& aNode);
00408   nsresult ProcessMAPTag(nsIContent* aContent);
00409   nsresult ProcessMETATag(const nsIParserNode& aNode);
00410   nsresult ProcessSCRIPTTag(const nsIParserNode& aNode);
00411   nsresult ProcessSTYLETag(const nsIParserNode& aNode);
00412 
00413   nsresult OpenHeadContext();
00414   nsresult CloseHeadContext();
00415 
00416   // nsContentSink overrides
00417   virtual void PreEvaluateScript();
00418   virtual void PostEvaluateScript();
00419 
00420   void UpdateAllContexts();
00421   void NotifyAppend(nsIContent* aContent,
00422                     PRUint32 aStartIndex);
00423   void NotifyInsert(nsIContent* aContent,
00424                     nsIContent* aChildContent,
00425                     PRInt32 aIndexInContainer);
00426   PRBool IsMonolithicContainer(nsHTMLTag aTag);
00427 
00428   // CanInterrupt parsing related routines
00429   nsresult AddDummyParserRequest(void);
00430   nsresult RemoveDummyParserRequest(void);
00431 
00432 #ifdef NS_DEBUG
00433   void ForceReflow();
00434 #endif
00435 
00436   // Measures content model creation time for current document
00437   MOZ_TIMER_DECLARE(mWatch)
00438 };
00439 
00440 
00441 //----------------------------------------------------------------------
00442 //
00443 // DummyParserRequest
00444 //
00445 //   This is a dummy request implementation that we add to the document's load
00446 //   group. It ensures that EndDocumentLoad() in the docshell doesn't fire
00447 //   before we've finished all of parsing and tokenizing of the document.
00448 //
00449 
00450 class DummyParserRequest : public nsIChannel
00451 {
00452 protected:
00453   DummyParserRequest(nsIHTMLContentSink* aSink);
00454   virtual ~DummyParserRequest();
00455 
00456   static PRInt32 gRefCnt;
00457   static nsIURI* gURI;
00458 
00459   nsCOMPtr<nsILoadGroup> mLoadGroup;
00460 
00461   nsWeakPtr mSink; // Weak reference
00462 
00463 public:
00464   static nsresult
00465   Create(nsIRequest** aResult, nsIHTMLContentSink* aSink);
00466 
00467   NS_DECL_ISUPPORTS
00468 
00469        // nsIRequest
00470   NS_IMETHOD GetName(nsACString &result)
00471   {
00472     result.AssignLiteral("about:layout-dummy-request");
00473     return NS_OK;
00474   }
00475 
00476   NS_IMETHOD IsPending(PRBool *_retval)
00477   {
00478     *_retval = PR_TRUE;
00479     return NS_OK;
00480   }
00481 
00482   NS_IMETHOD GetStatus(nsresult *status)
00483   {
00484     *status = NS_OK;
00485     return NS_OK;
00486   }
00487 
00488   NS_IMETHOD Cancel(nsresult status);
00489   NS_IMETHOD Suspend(void)
00490   {
00491     return NS_OK;
00492   }
00493 
00494   NS_IMETHOD Resume(void)
00495   {
00496     return NS_OK;
00497   }
00498 
00499   NS_IMETHOD GetLoadGroup(nsILoadGroup **aLoadGroup)
00500   {
00501     *aLoadGroup = mLoadGroup;
00502     NS_IF_ADDREF(*aLoadGroup);
00503 
00504     return NS_OK;
00505   }
00506 
00507   NS_IMETHOD SetLoadGroup(nsILoadGroup * aLoadGroup)
00508   {
00509     mLoadGroup = aLoadGroup;
00510 
00511     return NS_OK;
00512   }
00513 
00514   NS_IMETHOD GetLoadFlags(nsLoadFlags *aLoadFlags)
00515   {
00516     *aLoadFlags = nsIRequest::LOAD_NORMAL;
00517 
00518     return NS_OK;
00519   }
00520 
00521   NS_IMETHOD SetLoadFlags(nsLoadFlags aLoadFlags)
00522   {
00523     return NS_OK;
00524   }
00525 
00526        // nsIChannel
00527   NS_IMETHOD GetOriginalURI(nsIURI **aOriginalURI)
00528   {
00529     *aOriginalURI = gURI;
00530     NS_ADDREF(*aOriginalURI);
00531 
00532     return NS_OK;
00533   }
00534 
00535   NS_IMETHOD SetOriginalURI(nsIURI* aOriginalURI)
00536   {
00537     gURI = aOriginalURI;
00538     NS_ADDREF(gURI);
00539 
00540     return NS_OK;
00541   }
00542 
00543   NS_IMETHOD GetURI(nsIURI **aURI)
00544   {
00545     *aURI = gURI;
00546     NS_ADDREF(*aURI);
00547 
00548     return NS_OK;
00549   }
00550 
00551   NS_IMETHOD SetURI(nsIURI* aURI)
00552   {
00553     gURI = aURI;
00554     NS_ADDREF(gURI);
00555 
00556     return NS_OK;
00557   }
00558 
00559   NS_IMETHOD Open(nsIInputStream **_retval)
00560   {
00561     *_retval = nsnull;
00562 
00563     return NS_OK;
00564   }
00565 
00566   NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt)
00567   {
00568     return NS_OK;
00569   }
00570 
00571   NS_IMETHOD GetOwner(nsISupports **aOwner)
00572   {
00573     *aOwner = nsnull;
00574 
00575     return NS_OK;
00576   }
00577 
00578   NS_IMETHOD SetOwner(nsISupports *aOwner)
00579   {
00580     return NS_OK;
00581   }
00582 
00583   NS_IMETHOD GetNotificationCallbacks(nsIInterfaceRequestor **aNotifCallbacks)
00584   {
00585     *aNotifCallbacks = nsnull;
00586 
00587     return NS_OK;
00588   }
00589 
00590   NS_IMETHOD SetNotificationCallbacks(nsIInterfaceRequestor *aNotifCallbacks)
00591   {
00592     return NS_OK;
00593   }
00594 
00595   NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo)
00596   {
00597     *aSecurityInfo = nsnull;
00598 
00599     return NS_OK;
00600   }
00601 
00602   NS_IMETHOD GetContentType(nsACString &aContentType)
00603   {
00604     aContentType.Truncate();
00605 
00606     return NS_OK;
00607   }
00608 
00609   NS_IMETHOD SetContentType(const nsACString &aContentType)
00610   {
00611     return NS_OK;
00612   }
00613 
00614   NS_IMETHOD GetContentCharset(nsACString &aContentCharset)
00615   {
00616     aContentCharset.Truncate();
00617 
00618     return NS_OK;
00619   }
00620 
00621   NS_IMETHOD SetContentCharset(const nsACString &aContentCharset)
00622   {
00623     return NS_OK;
00624   }
00625 
00626   NS_IMETHOD GetContentLength(PRInt32 *aContentLength)
00627   {
00628     return NS_OK;
00629   }
00630 
00631   NS_IMETHOD SetContentLength(PRInt32 aContentLength)
00632   {
00633     return NS_OK;
00634   }
00635 };
00636 
00637 PRInt32 DummyParserRequest::gRefCnt;
00638 nsIURI* DummyParserRequest::gURI;
00639 
00640 NS_IMPL_ADDREF(DummyParserRequest)
00641 NS_IMPL_RELEASE(DummyParserRequest)
00642 NS_IMPL_QUERY_INTERFACE2(DummyParserRequest, nsIRequest, nsIChannel)
00643 
00644 nsresult
00645 DummyParserRequest::Create(nsIRequest** aResult, nsIHTMLContentSink* aSink)
00646 {
00647   *aResult = new DummyParserRequest(aSink);
00648   if (!*aResult) {
00649     return NS_ERROR_OUT_OF_MEMORY;
00650   }
00651 
00652   NS_ADDREF(*aResult);
00653 
00654   return NS_OK;
00655 }
00656 
00657 
00658 DummyParserRequest::DummyParserRequest(nsIHTMLContentSink* aSink)
00659 {
00660 
00661   if (gRefCnt++ == 0) {
00662 #ifdef DEBUG
00663     nsresult rv =
00664 #endif
00665     NS_NewURI(&gURI, NS_LITERAL_CSTRING("about:parser-dummy-request"));
00666 
00667     NS_ASSERTION(NS_SUCCEEDED(rv),
00668                  "unable to create about:parser-dummy-request");
00669   }
00670 
00671   mSink = do_GetWeakReference(aSink);
00672 }
00673 
00674 
00675 DummyParserRequest::~DummyParserRequest()
00676 {
00677   if (--gRefCnt == 0) {
00678     NS_IF_RELEASE(gURI);
00679   }
00680 }
00681 
00682 NS_IMETHODIMP
00683 DummyParserRequest::Cancel(nsresult status)
00684 {
00685   // Cancel parser
00686   nsresult rv = NS_OK;
00687   nsCOMPtr<nsIHTMLContentSink> sink = do_QueryReferent(mSink);
00688   if (sink) {
00689     nsIHTMLContentSink* actualSink = sink;
00690     HTMLContentSink* htmlContentSink = NS_STATIC_CAST(HTMLContentSink*, actualSink);
00691     if (htmlContentSink->mParser) {
00692       htmlContentSink->mParser->CancelParsingEvents();
00693     }
00694   }
00695   return rv;
00696 }
00697 
00698 
00699 class SinkContext
00700 {
00701 public:
00702   SinkContext(HTMLContentSink* aSink);
00703   ~SinkContext();
00704 
00705   nsresult Begin(nsHTMLTag aNodeType, nsGenericHTMLElement* aRoot,
00706                  PRUint32 aNumFlushed, PRInt32 aInsertionPoint);
00707   nsresult OpenContainer(const nsIParserNode& aNode);
00708   nsresult CloseContainer(const nsHTMLTag aTag);
00709   nsresult AddLeaf(const nsIParserNode& aNode);
00710   nsresult AddLeaf(nsGenericHTMLElement* aContent);
00711   nsresult AddComment(const nsIParserNode& aNode);
00712   nsresult End();
00713 
00714   nsresult GrowStack();
00715   nsresult AddText(const nsAString& aText);
00716   nsresult FlushText(PRBool* aDidFlush = nsnull,
00717                      PRBool aReleaseLast = PR_FALSE);
00718   nsresult FlushTextAndRelease(PRBool* aDidFlush = nsnull)
00719   {
00720     return FlushText(aDidFlush, PR_TRUE);
00721   }
00722 
00723   nsresult FlushTags(PRBool aNotify);
00724 
00725   PRBool   IsCurrentContainer(nsHTMLTag mType);
00726   PRBool   IsAncestorContainer(nsHTMLTag mType);
00727   nsGenericHTMLElement* GetCurrentContainer();
00728 
00729   void DidAddContent(nsIContent* aContent, PRBool aDidNotify = PR_FALSE);
00730   void UpdateChildCounts();
00731 
00732   HTMLContentSink* mSink;
00733   PRInt32 mNotifyLevel;
00734   nsCOMPtr<nsITextContent> mLastTextNode;
00735   PRInt32 mLastTextNodeSize;
00736 
00737   struct Node {
00738     nsHTMLTag mType;
00739     nsGenericHTMLElement* mContent;
00740     PRUint32 mNumFlushed;
00741     PRInt32 mInsertionPoint;
00742   };
00743 
00744   Node* mStack;
00745   PRInt32 mStackSize;
00746   PRInt32 mStackPos;
00747 
00748   PRUnichar* mText;
00749   PRInt32 mTextLength;
00750   PRInt32 mTextSize;
00751 };
00752 
00753 //----------------------------------------------------------------------
00754 
00755 #ifdef NS_DEBUG
00756 void
00757 HTMLContentSink::SinkTraceNode(PRUint32 aBit,
00758                                const char* aMsg,
00759                                const nsHTMLTag aTag,
00760                                PRInt32 aStackPos,
00761                                void* aThis)
00762 {
00763   if (SINK_LOG_TEST(gSinkLogModuleInfo, aBit)) {
00764     nsIParserService *parserService =
00765       nsContentUtils::GetParserServiceWeakRef();
00766     if (!parserService)
00767       return;
00768 
00769     NS_ConvertUTF16toUTF8 tag(parserService->HTMLIdToStringTag(aTag));
00770     PR_LogPrint("%s: this=%p node='%s' stackPos=%d", 
00771                 aMsg, aThis, tag.get(), aStackPos);
00772   }
00773 }
00774 #endif
00775 
00776 nsresult
00777 HTMLContentSink::AddAttributes(const nsIParserNode& aNode,
00778                                nsIContent* aContent, PRBool aNotify,
00779                                PRBool aCheckIfPresent)
00780 {
00781   // Add tag attributes to the content attributes
00782 
00783   PRInt32 ac = aNode.GetAttributeCount();
00784 
00785   if (ac == 0) {
00786     // No attributes, nothing to do. Do an early return to avoid
00787     // constructing the nsAutoString object for nothing.
00788 
00789     return NS_OK;
00790   }
00791 
00792   nsCAutoString k;
00793   nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
00794 
00795   // The attributes are on the parser node in the order they came in in the
00796   // source.  What we want to happen if a single attribute is set multiple
00797   // times on an element is that the first time should "win".  That is, <input
00798   // value="foo" value="bar"> should show "foo".  So we loop over the
00799   // attributes backwards; this ensures that the first attribute in the set
00800   // wins.  This does mean that we do some extra work in the case when the same
00801   // attribute is set multiple times, but we save a HasAttr call in the much
00802   // more common case of reasonable HTML.
00803   
00804   for (PRInt32 i = ac - 1; i >= 0; i--) {
00805     // Get lower-cased key
00806     const nsAString& key = aNode.GetKeyAt(i);
00807     // Copy up-front to avoid shared-buffer overhead (and convert to UTF-8
00808     // at the same time since that's what the atom table uses).
00809     CopyUTF16toUTF8(key, k);
00810     ToLowerCase(k);
00811 
00812     nsCOMPtr<nsIAtom> keyAtom = do_GetAtom(k);
00813 
00814     if (aCheckIfPresent && aContent->HasAttr(kNameSpaceID_None, keyAtom)) {
00815       continue;
00816     }
00817 
00818     // Get value and remove mandatory quotes
00819     static const char* kWhitespace = "\n\r\t\b";
00820 
00821     // Bug 114997: Don't trim whitespace on <input value="...">:
00822     // Using ?: outside the function call would be more efficient, but
00823     // we don't trust ?: with references.
00824     const nsAString& v =
00825       nsContentUtils::TrimCharsInSet(
00826         (nodeType == eHTMLTag_input &&
00827           keyAtom == nsHTMLAtoms::value) ?
00828         "" : kWhitespace, aNode.GetValueAt(i));
00829 
00830     if (nodeType == eHTMLTag_a && keyAtom == nsHTMLAtoms::name) {
00831       NS_ConvertUCS2toUTF8 cname(v);
00832       NS_ConvertUTF8toUCS2 uv(nsUnescape(cname.BeginWriting()));
00833 
00834       // Add attribute to content
00835       aContent->SetAttr(kNameSpaceID_None, keyAtom, uv, aNotify);
00836     } else {
00837       // Add attribute to content
00838       aContent->SetAttr(kNameSpaceID_None, keyAtom, v, aNotify);
00839     }
00840   }
00841 
00842   return NS_OK;
00843 }
00844 
00845 static void
00846 SetForm(nsGenericHTMLElement* aContent, nsGenericHTMLElement* aForm)
00847 {
00848   nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(aContent));
00849   NS_ASSERTION(formControl, "nsGenericHTMLElement didn't implement nsIFormControl");
00850   nsCOMPtr<nsIDOMHTMLFormElement> formElement(do_QueryInterface(aForm));
00851   NS_ASSERTION(!aForm || formElement, "nsGenericHTMLElement didn't implement nsIDOMHTMLFormElement");
00852 
00853   formControl->SetForm(formElement);
00854 }
00855 
00856 static already_AddRefed<nsGenericHTMLElement>
00857 MakeContentObject(nsHTMLTag aNodeType, nsINodeInfo *aNodeInfo,
00858                   nsGenericHTMLElement* aForm,
00859                   PRBool aInsideNoXXXTag, PRBool aFromParser);
00860 
00864 already_AddRefed<nsGenericHTMLElement>
00865 HTMLContentSink::CreateContentObject(const nsIParserNode& aNode,
00866                                      nsHTMLTag aNodeType,
00867                                      nsGenericHTMLElement* aForm,
00868                                      nsIDocShell* aDocShell)
00869 {
00870   // Find/create atom for the tag name
00871 
00872   nsCOMPtr<nsINodeInfo> nodeInfo;
00873 
00874   if (aNodeType == eHTMLTag_userdefined) {
00875     NS_ConvertUTF16toUTF8 tmp(aNode.GetText());
00876     ToLowerCase(tmp);
00877 
00878     nsCOMPtr<nsIAtom> name = do_GetAtom(tmp);
00879     mNodeInfoManager->GetNodeInfo(name, nsnull, kNameSpaceID_None,
00880                                   getter_AddRefs(nodeInfo));
00881   } else {
00882     nsIParserService *parserService =
00883       nsContentUtils::GetParserServiceWeakRef();
00884     if (!parserService)
00885       return nsnull;
00886 
00887     nsIAtom *name = parserService->HTMLIdToAtomTag(aNodeType);
00888     NS_ASSERTION(name, "What? Reverse mapping of id to string broken!!!");
00889 
00890     mNodeInfoManager->GetNodeInfo(name, nsnull, kNameSpaceID_None,
00891                                   getter_AddRefs(nodeInfo));
00892   }
00893 
00894   NS_ENSURE_TRUE(nodeInfo, nsnull);
00895 
00896   // Make the content object
00897   nsGenericHTMLElement* result = MakeContentObject(aNodeType, nodeInfo, aForm,
00898                                              !!mInsideNoXXXTag, PR_TRUE).get();
00899   if (!result) {
00900     return nsnull;
00901   }
00902 
00903   result->SetContentID(mDocument->GetAndIncrementContentID());
00904 
00905   return result;
00906 }
00907 
00908 nsresult
00909 NS_NewHTMLElement(nsIContent** aResult, nsINodeInfo *aNodeInfo)
00910 {
00911   *aResult = nsnull;
00912 
00913   nsIParserService* parserService = nsContentUtils::GetParserServiceWeakRef();
00914   if (!parserService)
00915     return NS_ERROR_OUT_OF_MEMORY;
00916 
00917   nsIAtom *name = aNodeInfo->NameAtom();
00918 
00919   nsHTMLTag id;
00920   nsRefPtr<nsGenericHTMLElement> result;
00921   if (aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
00922     // Find tag in tag table
00923     id = nsHTMLTag(parserService->HTMLCaseSensitiveAtomTagToId(name));
00924 
00925     result = MakeContentObject(id, aNodeInfo, nsnull, PR_FALSE, PR_FALSE);
00926   }
00927   else {
00928     // Find tag in tag table
00929     id = nsHTMLTag(parserService->HTMLAtomTagToId(name));
00930 
00931     // Reverse map id to name to get the correct character case in
00932     // the tag name.
00933 
00934     nsCOMPtr<nsINodeInfo> kungFuDeathGrip;
00935     nsINodeInfo *nodeInfo = aNodeInfo;
00936 
00937     if (id != eHTMLTag_userdefined) {
00938       nsIAtom *tag = parserService->HTMLIdToAtomTag(id);
00939       NS_ASSERTION(tag, "What? Reverse mapping of id to string broken!!!");
00940 
00941       if (name != tag) {
00942         nsresult rv =
00943           nsContentUtils::NameChanged(aNodeInfo, tag,
00944                                       getter_AddRefs(kungFuDeathGrip));
00945         NS_ENSURE_SUCCESS(rv, rv);
00946 
00947         nodeInfo = kungFuDeathGrip;
00948       }
00949     }
00950 
00951     result = MakeContentObject(id, nodeInfo, nsnull, PR_FALSE, PR_FALSE);
00952   }
00953 
00954   return result ? CallQueryInterface(result.get(), aResult)
00955     : NS_ERROR_OUT_OF_MEMORY;
00956 }
00957 
00958 already_AddRefed<nsGenericHTMLElement>
00959 MakeContentObject(nsHTMLTag aNodeType, nsINodeInfo *aNodeInfo,
00960                   nsGenericHTMLElement* aForm,
00961                   PRBool aInsideNoXXXTag, PRBool aFromParser)
00962 {
00963 
00964   if (aNodeType == eHTMLTag_form) {
00965     if (aForm) {
00966       // the form was already created
00967       NS_ADDREF(aForm);
00968       return aForm;
00969     }
00970     nsGenericHTMLElement* result = NS_NewHTMLFormElement(aNodeInfo);
00971     NS_IF_ADDREF(result);
00972     return result;
00973   }
00974 
00975   contentCreatorCallback cb = sContentCreatorCallbacks[aNodeType];
00976 
00977   NS_ASSERTION(cb != NS_NewHTMLNOTUSEDElement,
00978                "Don't know how to construct tag element!");
00979 
00980   nsGenericHTMLElement* result = cb(aNodeInfo, aFromParser);
00981   if (!result) {
00982     return nsnull;
00983   }
00984 
00985   NS_ADDREF(result);
00986 
00987   if (aForm && !aInsideNoXXXTag) {
00988     switch (aNodeType) {
00989     case eHTMLTag_button:
00990     case eHTMLTag_fieldset:
00991     case eHTMLTag_label:
00992     case eHTMLTag_legend:
00993     case eHTMLTag_object:
00994     case eHTMLTag_input:
00995     case eHTMLTag_select:
00996     case eHTMLTag_textarea:
00997       SetForm(result, aForm);
00998       break;
00999     default:
01000       break;
01001     }
01002   }
01003 
01004   return result;
01005 }
01006 
01007 //----------------------------------------------------------------------
01008 
01009 MOZ_DECL_CTOR_COUNTER(SinkContext)
01010 
01011 SinkContext::SinkContext(HTMLContentSink* aSink)
01012   : mSink(aSink),
01013     mNotifyLevel(0),
01014     mLastTextNodeSize(0),
01015     mStack(nsnull),
01016     mStackSize(0),
01017     mStackPos(0),
01018     mText(nsnull),
01019     mTextLength(0),
01020     mTextSize(0)
01021 {
01022   MOZ_COUNT_CTOR(SinkContext);
01023 }
01024 
01025 SinkContext::~SinkContext()
01026 {
01027   MOZ_COUNT_DTOR(SinkContext);
01028 
01029   if (mStack) {
01030     for (PRInt32 i = 0; i < mStackPos; i++) {
01031       NS_RELEASE(mStack[i].mContent);
01032     }
01033     delete [] mStack;
01034   }
01035 
01036   delete [] mText;
01037 }
01038 
01039 nsresult
01040 SinkContext::Begin(nsHTMLTag aNodeType,
01041                    nsGenericHTMLElement* aRoot,
01042                    PRUint32 aNumFlushed,
01043                    PRInt32 aInsertionPoint)
01044 {
01045   if (mStackSize < 1) {
01046     nsresult rv = GrowStack();
01047     if (NS_FAILED(rv)) {
01048       return rv;
01049     }
01050   }
01051 
01052   mStack[0].mType = aNodeType;
01053   mStack[0].mContent = aRoot;
01054   mStack[0].mNumFlushed = aNumFlushed;
01055   mStack[0].mInsertionPoint = aInsertionPoint;
01056   NS_ADDREF(aRoot);
01057   mStackPos = 1;
01058   mTextLength = 0;
01059 
01060   return NS_OK;
01061 }
01062 
01063 PRBool
01064 SinkContext::IsCurrentContainer(nsHTMLTag aTag)
01065 {
01066   if (aTag == mStack[mStackPos - 1].mType) {
01067     return PR_TRUE;
01068   }
01069 
01070   return PR_FALSE;
01071 }
01072 
01073 PRBool
01074 SinkContext::IsAncestorContainer(nsHTMLTag aTag)
01075 {
01076   PRInt32 stackPos = mStackPos - 1;
01077 
01078   while (stackPos >= 0) {
01079     if (aTag == mStack[stackPos].mType) {
01080       return PR_TRUE;
01081     }
01082     stackPos--;
01083   }
01084 
01085   return PR_FALSE;
01086 }
01087 
01088 nsGenericHTMLElement*
01089 SinkContext::GetCurrentContainer()
01090 {
01091   nsGenericHTMLElement* content = mStack[mStackPos - 1].mContent;
01092   NS_ADDREF(content);
01093 
01094   return content;
01095 }
01096 
01097 void
01098 SinkContext::DidAddContent(nsIContent* aContent, PRBool aDidNotify)
01099 {
01100   // If there was a notification done for this content, update the
01101   // parent's notification count.
01102   if (aDidNotify && (0 < mStackPos)) {
01103     nsIContent* parent = mStack[mStackPos - 1].mContent;
01104     mStack[mStackPos - 1].mNumFlushed = parent->GetChildCount();
01105   }
01106 
01107   if ((mStackPos == 2) && (mSink->mBody == mStack[1].mContent ||
01108                            mSink->mFrameset == mStack[1].mContent)) {
01109     // We just finished adding something to the body
01110     mNotifyLevel = 0;
01111   }
01112 
01113   // If we just added content to a node for which
01114   // an insertion happen, we need to do an immediate
01115   // notification for that insertion.
01116   if (!aDidNotify && (0 < mStackPos) &&
01117       (mStack[mStackPos - 1].mInsertionPoint != -1)) {
01118     nsIContent* parent = mStack[mStackPos - 1].mContent;
01119 
01120 #ifdef NS_DEBUG
01121     // Tracing code
01122     nsIParserService *parserService =
01123       nsContentUtils::GetParserServiceWeakRef();
01124     if (parserService) {
01125       nsHTMLTag tag = nsHTMLTag(mStack[mStackPos - 1].mType);
01126       NS_ConvertUTF16toUTF8 str(parserService->HTMLIdToStringTag(tag));
01127 
01128       SINK_TRACE(SINK_TRACE_REFLOW,
01129                  ("SinkContext::DidAddContent: Insertion notification for "
01130                   "parent=%s at position=%d and stackPos=%d",
01131                   str.get(), mStack[mStackPos - 1].mInsertionPoint - 1,
01132                   mStackPos - 1));
01133     }
01134 #endif
01135 
01136     mSink->NotifyInsert(parent, aContent,
01137                         mStack[mStackPos - 1].mInsertionPoint - 1);
01138     mStack[mStackPos - 1].mNumFlushed = parent->GetChildCount();
01139   } else if (!aDidNotify && mSink->IsTimeToNotify()) {
01140     SINK_TRACE(SINK_TRACE_REFLOW,
01141                ("SinkContext::DidAddContent: Notification as a result of the "
01142                 "interval expiring; backoff count: %d", mSink->mBackoffCount));
01143     FlushTags(PR_TRUE);
01144   }
01145 }
01146 
01147 nsresult
01148 SinkContext::OpenContainer(const nsIParserNode& aNode)
01149 {
01150   FlushTextAndRelease();
01151 
01152   SINK_TRACE_NODE(SINK_TRACE_CALLS,
01153                   "SinkContext::OpenContainer", 
01154                   nsHTMLTag(aNode.GetNodeType()), 
01155                   mStackPos, 
01156                   mSink);
01157 
01158   if (mStackPos <= 0) {
01159     NS_ERROR("container w/o parent");
01160 
01161     return NS_ERROR_FAILURE;
01162   }
01163 
01164   nsresult rv;
01165   if (mStackPos + 1 > mStackSize) {
01166     rv = GrowStack();
01167     if (NS_FAILED(rv)) {
01168       return rv;
01169     }
01170   }
01171 
01172   // Create new container content object
01173   nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
01174   nsIDocShell *docshell = nsnull;
01175   if (mSink->mFrameset) docshell = (nsIDocShell *) mSink->mDocShell;
01176   nsGenericHTMLElement* content =
01177     mSink->CreateContentObject(aNode, nodeType, mSink->mCurrentForm,
01178                                docshell).get();
01179   if (!content) {
01180     return NS_ERROR_OUT_OF_MEMORY;
01181   }
01182 
01183   mStack[mStackPos].mType = nodeType;
01184   mStack[mStackPos].mContent = content;
01185   mStack[mStackPos].mNumFlushed = 0;
01186   mStack[mStackPos].mInsertionPoint = -1;
01187   ++mStackPos;
01188 
01189   // Make sure to add base tag info, if needed, before setting any other
01190   // attributes -- what URI attrs do will depend on the base URI.  Only do this
01191   // for elements that have useful URI attributes.
01192   // See bug 18478 and bug 30617 for why we need to do this.
01193   switch (nodeType) {
01194     // Containers with "href="
01195     case eHTMLTag_a:
01196     case eHTMLTag_map:
01197     
01198     // Containers with "action="
01199     case eHTMLTag_form:
01200 
01201     // Containers with "data="
01202     case eHTMLTag_object:
01203 
01204     // Containers with "background="
01205     case eHTMLTag_table:
01206     case eHTMLTag_thead:
01207     case eHTMLTag_tbody:
01208     case eHTMLTag_tfoot:
01209     case eHTMLTag_tr:
01210     case eHTMLTag_td:
01211     case eHTMLTag_th:
01212       mSink->AddBaseTagInfo(content);
01213 
01214       break;
01215     default:
01216       break;    
01217   }
01218   
01219   rv = mSink->AddAttributes(aNode, content);
01220 
01221   nsGenericHTMLElement* parent = mStack[mStackPos - 2].mContent;
01222 
01223   if (mStack[mStackPos - 2].mInsertionPoint != -1) {
01224     parent->InsertChildAt(content,
01225                           mStack[mStackPos - 2].mInsertionPoint++,
01226                           PR_FALSE);
01227   } else {
01228     parent->AppendChildTo(content, PR_FALSE);
01229   }
01230 
01231   NS_ENSURE_SUCCESS(rv, rv);
01232 
01233   if (mSink->IsMonolithicContainer(nodeType)) {
01234     mSink->mInMonolithicContainer++;
01235   }
01236 
01237   // Special handling for certain tags
01238   switch (nodeType) {
01239     case eHTMLTag_noembed:
01240     case eHTMLTag_noframes:
01241       mSink->mInsideNoXXXTag++;
01242 
01243       break;
01244 
01245     case eHTMLTag_map:
01246       mSink->ProcessMAPTag(content);
01247       break;
01248     case eHTMLTag_iframe:
01249       mSink->mNumOpenIFRAMES++;
01250 
01251       break;
01252     default:
01253       break;
01254   }
01255 
01256   return NS_OK;
01257 }
01258 
01259 nsresult
01260 SinkContext::CloseContainer(const nsHTMLTag aTag)
01261 {
01262   nsresult result = NS_OK;
01263 
01264   // Flush any collected text content. Release the last text
01265   // node to indicate that no more should be added to it.
01266   FlushTextAndRelease();
01267 
01268   SINK_TRACE_NODE(SINK_TRACE_CALLS,
01269                   "SinkContext::CloseContainer", 
01270                   aTag, mStackPos - 1, mSink);
01271 
01272   NS_WARN_IF_FALSE(mStackPos > 0,
01273                    "stack out of bounds. wrong context probably!");
01274 
01275   if (mStackPos <= 0) {
01276     return NS_OK; // Fix crash - Ref. bug 45975 or 45007
01277   }
01278 
01279   --mStackPos;
01280   nsHTMLTag nodeType = mStack[mStackPos].mType;
01281 
01282   nsGenericHTMLElement* content = mStack[mStackPos].mContent;
01283 
01284   content->Compact();
01285 
01286   // If we're in a state where we do append notifications as
01287   // we go up the tree, and we're at the level where the next
01288   // notification needs to be done, do the notification.
01289   if (mNotifyLevel >= mStackPos) {
01290     // Check to see if new content has been added after our last
01291     // notification
01292 
01293     if (mStack[mStackPos].mNumFlushed < content->GetChildCount()) {
01294 #ifdef NS_DEBUG
01295       {
01296         // Tracing code
01297         const char *tagStr;
01298         mStack[mStackPos].mContent->Tag()->GetUTF8String(&tagStr);
01299 
01300         SINK_TRACE(SINK_TRACE_REFLOW,
01301                    ("SinkContext::CloseContainer: reflow on notifyImmediate "
01302                     "tag=%s newIndex=%d stackPos=%d",
01303                     tagStr,
01304                     mStack[mStackPos].mNumFlushed, mStackPos));
01305       }
01306 #endif
01307       mSink->NotifyAppend(content, mStack[mStackPos].mNumFlushed);
01308     }
01309 
01310     // Indicate that notification has now happened at this level
01311     mNotifyLevel = mStackPos - 1;
01312   }
01313 
01314   if (mSink->IsMonolithicContainer(nodeType)) {
01315     --mSink->mInMonolithicContainer;
01316   }
01317 
01318   DidAddContent(content, PR_FALSE);
01319 
01320   // Special handling for certain tags
01321   switch (nodeType) {
01322   case eHTMLTag_noembed:
01323   case eHTMLTag_noframes:
01324     // Fix bug 40216
01325     NS_ASSERTION((mSink->mInsideNoXXXTag > 0), "mInsideNoXXXTag underflow");
01326     if (mSink->mInsideNoXXXTag > 0) {
01327       mSink->mInsideNoXXXTag--;
01328     }
01329 
01330     break;
01331   case eHTMLTag_form:
01332     {
01333       mSink->mFlags &= ~NS_SINK_FLAG_FORM_ON_STACK;
01334       // If there's a FORM on the stack, but this close tag doesn't
01335       // close the form, then close out the form *and* close out the
01336       // next container up. This is since the parser doesn't do fix up
01337       // of invalid form nesting. When the end FORM tag comes through,
01338       // we'll ignore it.
01339       if (aTag != nodeType) {
01340         result = CloseContainer(aTag);
01341       }
01342     }
01343 
01344     break;
01345   case eHTMLTag_iframe:
01346     mSink->mNumOpenIFRAMES--;
01347 
01348     break;
01349   case eHTMLTag_select:
01350   case eHTMLTag_textarea:
01351   case eHTMLTag_object:
01352   case eHTMLTag_applet:
01353     content->DoneAddingChildren();
01354 
01355     break;
01356   default:
01357     break;
01358   }
01359 
01360   NS_IF_RELEASE(content);
01361 
01362 #ifdef DEBUG
01363   if (SINK_LOG_TEST(gSinkLogModuleInfo, SINK_ALWAYS_REFLOW)) {
01364     mSink->ForceReflow();
01365   }
01366 #endif
01367 
01368   return result;
01369 }
01370 
01371 nsresult
01372 SinkContext::AddLeaf(const nsIParserNode& aNode)
01373 {
01374   SINK_TRACE_NODE(SINK_TRACE_CALLS,
01375                   "SinkContext::AddLeaf", 
01376                   nsHTMLTag(aNode.GetNodeType()), 
01377                   mStackPos, mSink);
01378 
01379   nsresult rv = NS_OK;
01380 
01381   switch (aNode.GetTokenType()) {
01382   case eToken_start:
01383     {
01384       FlushTextAndRelease();
01385 
01386       // Create new leaf content object
01387       nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
01388       nsRefPtr<nsGenericHTMLElement> content =
01389         mSink->CreateContentObject(aNode, nodeType,
01390                                    mSink->mCurrentForm, mSink->mDocShell);
01391       NS_ENSURE_TRUE(content, NS_ERROR_OUT_OF_MEMORY);
01392 
01393       // Make sure to add base tag info, if needed, before setting any other
01394       // attributes -- what URI attrs do will depend on the base URI.  Only do
01395       // this for elements that have useful URI attributes.
01396       // See bug 18478 and bug 30617 for why we need to do this.
01397       switch (nodeType) {
01398       // leaves with 'SRC='
01399       case eHTMLTag_img:
01400       case eHTMLTag_frame:
01401       case eHTMLTag_input:
01402       case eHTMLTag_embed:
01403       // <form> can end up as a leaf if it's misnested with table elements
01404       case eHTMLTag_form:
01405         mSink->AddBaseTagInfo(content);
01406 
01407         break;
01408       default:
01409         break;
01410       }
01411 
01412       rv = mSink->AddAttributes(aNode, content);
01413 
01414       NS_ENSURE_SUCCESS(rv, rv);
01415 
01416       // Add new leaf to its parent
01417       AddLeaf(content);
01418 
01419       // Notify input and button that they are now fully created
01420       switch (nodeType) {
01421       case eHTMLTag_input:
01422       case eHTMLTag_button:
01423         content->DoneCreatingElement();
01424 
01425         break;
01426 
01427       default:
01428         break;
01429       }
01430     }
01431     break;
01432 
01433   case eToken_text:
01434   case eToken_whitespace:
01435   case eToken_newline:
01436     rv = AddText(aNode.GetText());
01437 
01438     break;
01439   case eToken_entity:
01440     {
01441       nsAutoString tmp;
01442       PRInt32 unicode = aNode.TranslateToUnicodeStr(tmp);
01443       if (unicode < 0) {
01444         rv = AddText(aNode.GetText());
01445       } else {
01446         // Map carriage returns to newlines
01447         if (!tmp.IsEmpty()) {
01448           if (tmp.CharAt(0) == '\r') {
01449             tmp.Assign((PRUnichar)'\n');
01450           }
01451           rv = AddText(tmp);
01452         }
01453       }
01454     }
01455 
01456     break;
01457   default:
01458     break;
01459   }
01460 
01461   return rv;
01462 }
01463 
01464 nsresult
01465 SinkContext::AddLeaf(nsGenericHTMLElement* aContent)
01466 {
01467   NS_ASSERTION(mStackPos > 0, "leaf w/o container");
01468   if (mStackPos <= 0) {
01469     return NS_ERROR_FAILURE;
01470   }
01471 
01472   nsGenericHTMLElement* parent = mStack[mStackPos - 1].mContent;
01473 
01474   // If the parent has an insertion point, insert rather than append.
01475   if (mStack[mStackPos - 1].mInsertionPoint != -1) {
01476     parent->InsertChildAt(aContent,
01477                           mStack[mStackPos - 1].mInsertionPoint++,
01478                           PR_FALSE);
01479   } else {
01480     parent->AppendChildTo(aContent, PR_FALSE);
01481   }
01482 
01483   DidAddContent(aContent, PR_FALSE);
01484 
01485 #ifdef DEBUG
01486   if (SINK_LOG_TEST(gSinkLogModuleInfo, SINK_ALWAYS_REFLOW)) {
01487     mSink->ForceReflow();
01488   }
01489 #endif
01490 
01491   return NS_OK;
01492 }
01493 
01494 nsresult
01495 SinkContext::AddComment(const nsIParserNode& aNode)
01496 {
01497   SINK_TRACE_NODE(SINK_TRACE_CALLS,
01498                   "SinkContext::AddLeaf", 
01499                   nsHTMLTag(aNode.GetNodeType()), 
01500                   mStackPos, mSink);
01501   FlushTextAndRelease();
01502 
01503   if (!mSink) {
01504     return NS_ERROR_UNEXPECTED;
01505   }
01506   
01507   nsCOMPtr<nsIContent> comment;
01508   nsresult rv = NS_NewCommentNode(getter_AddRefs(comment),
01509                                   mSink->mNodeInfoManager);
01510   NS_ENSURE_SUCCESS(rv, rv);
01511 
01512   nsCOMPtr<nsIDOMComment> domComment(do_QueryInterface(comment));
01513   NS_ENSURE_TRUE(domComment, NS_ERROR_UNEXPECTED);
01514 
01515   domComment->AppendData(aNode.GetText());
01516 
01517   NS_ASSERTION(mStackPos > 0, "stack out of bounds");
01518   if (mStackPos <= 0) {
01519     return NS_ERROR_FAILURE;
01520   }
01521 
01522   nsGenericHTMLElement* parent;
01523   if (!mSink->mBody && !mSink->mFrameset && mSink->mHead) {
01524     parent = mSink->mHead;
01525   } else {
01526     parent = mStack[mStackPos - 1].mContent;
01527   }
01528 
01529   // If the parent has an insertion point, insert rather than append.
01530   if (mStack[mStackPos - 1].mInsertionPoint != -1) {
01531     parent->InsertChildAt(comment,
01532                           mStack[mStackPos - 1].mInsertionPoint++,
01533                           PR_FALSE);
01534   } else {
01535     parent->AppendChildTo(comment, PR_FALSE);
01536   }
01537 
01538   DidAddContent(comment, PR_FALSE);
01539 
01540 #ifdef DEBUG
01541   if (SINK_LOG_TEST(gSinkLogModuleInfo, SINK_ALWAYS_REFLOW)) {
01542     mSink->ForceReflow();
01543   }
01544 #endif
01545 
01546   return rv;
01547 }
01548 
01549 nsresult
01550 SinkContext::End()
01551 {
01552   for (PRInt32 i = 0; i < mStackPos; i++) {
01553     NS_RELEASE(mStack[i].mContent);
01554   }
01555 
01556   mStackPos = 0;
01557   mTextLength = 0;
01558 
01559   return NS_OK;
01560 }
01561 
01562 nsresult
01563 SinkContext::GrowStack()
01564 {
01565   PRInt32 newSize = mStackSize * 2;
01566   if (newSize == 0) {
01567     newSize = 32;
01568   }
01569 
01570   Node* stack = new Node[newSize];
01571   if (!stack) {
01572     return NS_ERROR_OUT_OF_MEMORY;
01573   }
01574 
01575   if (mStackPos != 0) {
01576     memcpy(stack, mStack, sizeof(Node) * mStackPos);
01577     delete [] mStack;
01578   }
01579 
01580   mStack = stack;
01581   mStackSize = newSize;
01582 
01583   return NS_OK;
01584 }
01585 
01592 // XXX If we get a giant string grow the buffer instead of chopping it
01593 // up???
01594 nsresult
01595 SinkContext::AddText(const nsAString& aText)
01596 {
01597   PRInt32 addLen = aText.Length();
01598   if (addLen == 0) {
01599     return NS_OK;
01600   }
01601 
01602   // Create buffer when we first need it
01603   if (mTextSize == 0) {
01604     mText = new PRUnichar[4096];
01605     if (!mText) {
01606       return NS_ERROR_OUT_OF_MEMORY;
01607     }
01608     mTextSize = 4096;
01609   }
01610 
01611   // Copy data from string into our buffer; flush buffer when it fills up
01612   PRInt32 offset = 0;
01613   PRBool  isLastCharCR = PR_FALSE;
01614 
01615   while (addLen != 0) {
01616     PRInt32 amount = mTextSize - mTextLength;
01617 
01618     if (amount > addLen) {
01619       amount = addLen;
01620     }
01621 
01622     if (amount == 0) {
01623       // Don't release last text node so we can add to it again
01624       nsresult rv = FlushText();
01625       if (NS_FAILED(rv)) {
01626         return rv;
01627       }
01628     }
01629 
01630     mTextLength +=
01631       nsContentUtils::CopyNewlineNormalizedUnicodeTo(aText, offset,
01632                                                      &mText[mTextLength],
01633                                                      amount, isLastCharCR);
01634     offset += amount;
01635     addLen -= amount;
01636   }
01637 
01638   return NS_OK;
01639 }
01640 
01648 nsresult
01649 SinkContext::FlushTags(PRBool aNotify)
01650 {
01651   // Not starting an update here, unlike trunk.  We'll get XBL
01652   // constructors firing async of the stuff we flush right now.
01653    
01654   // Don't release last text node in case we need to add to it again
01655   FlushText();
01656 
01657   if (aNotify) {
01658     // Start from the base of the stack (growing upward) and do
01659     // a notification from the node that is closest to the root of
01660     // tree for any content that has been added.
01661     PRInt32 stackPos = 1;
01662     PRBool flushed = PR_FALSE;
01663     PRUint32 childCount;
01664     nsGenericHTMLElement* content;
01665 
01666     while (stackPos < mStackPos) {
01667       content = mStack[stackPos].mContent;
01668       childCount = content->GetChildCount();
01669 
01670       if (!flushed && (mStack[stackPos].mNumFlushed < childCount)) {
01671 #ifdef NS_DEBUG
01672         {
01673           // Tracing code
01674           const char* tagStr;
01675           mStack[stackPos].mContent->Tag()->GetUTF8String(&tagStr);
01676 
01677           SINK_TRACE(SINK_TRACE_REFLOW,
01678                      ("SinkContext::FlushTags: tag=%s from newindex=%d at "
01679                       "stackPos=%d", tagStr,
01680                       mStack[stackPos].mNumFlushed, stackPos));
01681         }
01682 #endif
01683         if ((mStack[stackPos].mInsertionPoint != -1) &&
01684             (mStackPos > (stackPos + 1))) {
01685           nsIContent* child = mStack[stackPos + 1].mContent;
01686           mSink->NotifyInsert(content,
01687                               child,
01688                               mStack[stackPos].mInsertionPoint);
01689         } else {
01690           mSink->NotifyAppend(content, mStack[stackPos].mNumFlushed);
01691         }
01692 
01693         flushed = PR_TRUE;
01694       }
01695 
01696       mStack[stackPos].mNumFlushed = childCount;
01697       stackPos++;
01698     }
01699     mNotifyLevel = mStackPos - 1;
01700   }
01701 
01702   return NS_OK;
01703 }
01704 
01705 void
01706 SinkContext::UpdateChildCounts()
01707 {
01708   // Start from the top of the stack (growing upwards) and see if any
01709   // new content has been appended. If so, we recognize that reflows
01710   // have been generated for it and we should make sure that no
01711   // further reflows occur.
01712   PRInt32 stackPos = mStackPos - 1;
01713   while (stackPos > 0) {
01714     Node & node = mStack[stackPos];
01715     node.mNumFlushed = node.mContent->GetChildCount();
01716 
01717     stackPos--;
01718   }
01719 
01720   mNotifyLevel = mStackPos - 1;
01721 }
01722 
01727 nsresult
01728 SinkContext::FlushText(PRBool* aDidFlush, PRBool aReleaseLast)
01729 {
01730   nsresult rv = NS_OK;
01731   PRBool didFlush = PR_FALSE;
01732 
01733   if (mTextLength != 0) {
01734     if (mLastTextNode) {
01735       if ((mLastTextNodeSize + mTextLength) > mSink->mMaxTextRun) {
01736         mLastTextNodeSize = 0;
01737         mLastTextNode = nsnull;
01738         FlushText(aDidFlush, aReleaseLast);
01739       } else {
01740         nsCOMPtr<nsIDOMCharacterData> cdata(do_QueryInterface(mLastTextNode));
01741 
01742         if (cdata) {
01743           rv = cdata->AppendData(Substring(mText, mText + mTextLength));
01744 
01745           mLastTextNodeSize += mTextLength;
01746           mTextLength = 0;
01747           didFlush = PR_TRUE;
01748         }
01749       }
01750     } else {
01751       nsCOMPtr<nsITextContent> textContent;
01752       rv = NS_NewTextNode(getter_AddRefs(textContent),
01753                           mSink->mNodeInfoManager);
01754       NS_ENSURE_SUCCESS(rv, rv);
01755 
01756       mLastTextNode = textContent;
01757 
01758       // Set the text in the text node
01759       mLastTextNode->SetText(mText, mTextLength, PR_FALSE);
01760 
01761       // Eat up the rest of the text up in state.
01762       mLastTextNodeSize += mTextLength;
01763       mTextLength = 0;
01764 
01765       // Add text to its parent
01766       NS_ASSERTION(mStackPos > 0, "leaf w/o container");
01767       if (mStackPos <= 0) {
01768         return NS_ERROR_FAILURE;
01769       }
01770 
01771       nsGenericHTMLElement* parent = mStack[mStackPos - 1].mContent;
01772       if (mStack[mStackPos - 1].mInsertionPoint != -1) {
01773         parent->InsertChildAt(mLastTextNode,
01774                               mStack[mStackPos - 1].mInsertionPoint++,
01775                               PR_FALSE);
01776       } else {
01777         parent->AppendChildTo(mLastTextNode, PR_FALSE);
01778       }
01779 
01780       didFlush = PR_TRUE;
01781 
01782       DidAddContent(mLastTextNode, PR_FALSE);
01783     }
01784   }
01785 
01786   if (aDidFlush) {
01787     *aDidFlush = didFlush;
01788   }
01789 
01790   if (aReleaseLast) {
01791     mLastTextNodeSize = 0;
01792     mLastTextNode = nsnull;
01793   }
01794 
01795 #ifdef DEBUG
01796   if (didFlush &&
01797       SINK_LOG_TEST(gSinkLogModuleInfo, SINK_ALWAYS_REFLOW)) {
01798     mSink->ForceReflow();
01799   }
01800 #endif
01801 
01802   return rv;
01803 }
01804 
01805 
01806 nsresult
01807 NS_NewHTMLContentSink(nsIHTMLContentSink** aResult,
01808                       nsIDocument* aDoc,
01809                       nsIURI* aURI,
01810                       nsISupports* aContainer,
01811                       nsIChannel* aChannel)
01812 {
01813   NS_ENSURE_ARG_POINTER(aResult);
01814 
01815   nsRefPtr<HTMLContentSink> it = new HTMLContentSink();
01816 
01817   if (!it) {
01818     return NS_ERROR_OUT_OF_MEMORY;
01819   }
01820 
01821   nsresult rv = it->Init(aDoc, aURI, aContainer, aChannel);
01822 
01823   NS_ENSURE_SUCCESS(rv, rv);
01824 
01825   *aResult = it;
01826   NS_ADDREF(*aResult);
01827 
01828   return NS_OK;
01829 }
01830 
01831 HTMLContentSink::HTMLContentSink()
01832 {
01833   // Note: operator new zeros our memory
01834 
01835 
01836 #ifdef NS_DEBUG
01837   if (!gSinkLogModuleInfo) {
01838     gSinkLogModuleInfo = PR_NewLogModule("htmlcontentsink");
01839   }
01840 #endif
01841 }
01842 
01843 HTMLContentSink::~HTMLContentSink()
01844 {
01845   NS_IF_RELEASE(mHead);
01846   NS_IF_RELEASE(mBody);
01847   NS_IF_RELEASE(mFrameset);
01848   NS_IF_RELEASE(mRoot);
01849 
01850   if (mDocument) {
01851     mDocument->RemoveObserver(this);
01852   }
01853   NS_IF_RELEASE(mHTMLDocument);
01854 
01855   if (mNotificationTimer) {
01856     mNotificationTimer->Cancel();
01857   }
01858 
01859   PRInt32 numContexts = mContextStack.Count();
01860 
01861   if (mCurrentContext == mHeadContext && numContexts > 0) {
01862     // Pop off the second html context if it's not done earlier
01863     mContextStack.RemoveElementAt(--numContexts);
01864   }
01865 
01866   for (PRInt32 i = 0; i < numContexts; i++) {
01867     SinkContext* sc = (SinkContext*)mContextStack.ElementAt(i);
01868     if (sc) {
01869       sc->End();
01870       if (sc == mCurrentContext) {
01871         mCurrentContext = nsnull;
01872       }
01873 
01874       delete sc;
01875     }
01876   }
01877 
01878   if (mCurrentContext == mHeadContext) {
01879     mCurrentContext = nsnull;
01880   }
01881 
01882   delete mCurrentContext;
01883 
01884   delete mHeadContext;
01885 }
01886 
01887 #if DEBUG
01888 NS_IMPL_ISUPPORTS_INHERITED5(HTMLContentSink,
01889                              nsContentSink,
01890                              nsIContentSink,
01891                              nsIHTMLContentSink,
01892                              nsITimerCallback,
01893                              nsIDocumentObserver,
01894                              nsIDebugDumpContent)
01895 #else
01896 NS_IMPL_ISUPPORTS_INHERITED4(HTMLContentSink,
01897                              nsContentSink,
01898                              nsIContentSink,
01899                              nsIHTMLContentSink,
01900                              nsITimerCallback,
01901                              nsIDocumentObserver)
01902 #endif
01903 
01904 static PRBool
01905 IsScriptEnabled(nsIDocument *aDoc, nsIDocShell *aContainer)
01906 {
01907   NS_ENSURE_TRUE(aDoc && aContainer, PR_TRUE);
01908 
01909   nsIPrincipal *principal = aDoc->GetPrincipal();
01910   NS_ENSURE_TRUE(principal, PR_TRUE);
01911 
01912   nsCOMPtr<nsIScriptGlobalObject> globalObject = aDoc->GetScriptGlobalObject();
01913 
01914   // Getting context is tricky if the document hasn't had it's
01915   // GlobalObject set yet
01916   if (!globalObject) {
01917     nsCOMPtr<nsIScriptGlobalObjectOwner> owner = do_GetInterface(aContainer);
01918     NS_ENSURE_TRUE(owner, PR_TRUE);
01919 
01920     globalObject = owner->GetScriptGlobalObject();
01921     NS_ENSURE_TRUE(globalObject, PR_TRUE);
01922   }
01923 
01924   nsIScriptContext *scriptContext = globalObject->GetContext();
01925   NS_ENSURE_TRUE(scriptContext, PR_TRUE);
01926 
01927   JSContext* cx = (JSContext *) scriptContext->GetNativeContext();
01928   NS_ENSURE_TRUE(cx, PR_TRUE);
01929 
01930   PRBool enabled = PR_TRUE;
01931   nsContentUtils::GetSecurityManager()->CanExecuteScripts(cx,
01932                                                           principal,
01933                                                           &enabled);
01934   return enabled;
01935 }
01936 
01937 nsresult
01938 HTMLContentSink::Init(nsIDocument* aDoc,
01939                       nsIURI* aURI,
01940                       nsISupports* aContainer,
01941                       nsIChannel* aChannel)
01942 {
01943   NS_ENSURE_TRUE(aContainer, NS_ERROR_NULL_POINTER);
01944 
01945 
01946   MOZ_TIMER_DEBUGLOG(("Reset and start: nsHTMLContentSink::Init(), this=%p\n",
01947                       this));
01948   MOZ_TIMER_RESET(mWatch);
01949   MOZ_TIMER_START(mWatch);
01950 
01951   nsresult rv = nsContentSink::Init(aDoc, aURI, aContainer, aChannel);
01952   if NS_FAILED(rv) {
01953     MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::Init()\n"));
01954     MOZ_TIMER_STOP(mWatch);
01955     return rv;
01956   }
01957 
01958   aDoc->AddObserver(this);
01959   CallQueryInterface(aDoc, &mHTMLDocument);
01960 
01961   mObservers = nsnull;
01962   nsIParserService* service = nsContentUtils::GetParserServiceWeakRef();
01963   if (!service) {
01964     return NS_ERROR_OUT_OF_MEMORY;
01965   }
01966 
01967   service->GetTopicObservers(NS_LITERAL_STRING("text/html"),
01968                              getter_AddRefs(mObservers));
01969 
01970   NS_WARN_IF_FALSE(mDocShell, "oops no docshell!");
01971 
01972   // Find out if subframes are enabled
01973   if (mDocShell) {
01974     PRBool subFramesEnabled = PR_TRUE;
01975     mDocShell->GetAllowSubframes(&subFramesEnabled);
01976     if (subFramesEnabled) {
01977       mFlags |= NS_SINK_FLAG_FRAMES_ENABLED;
01978     }
01979   }
01980 
01981   // Find out if scripts are enabled, if not, show <noscript> content
01982   if (IsScriptEnabled(aDoc, mDocShell)) {
01983     mFlags |= NS_SINK_FLAG_SCRIPT_ENABLED;
01984   }
01985 
01986   mNotifyOnTimer =
01987     nsContentUtils::GetBoolPref("content.notify.ontimer", PR_TRUE);
01988 
01989   // -1 means never
01990   mBackoffCount =
01991     nsContentUtils::GetIntPref("content.notify.backoffcount", -1);
01992 
01993   // The mNotificationInterval has a dramatic effect on how long it
01994   // takes to initially display content for slow connections.
01995   // The current value provides good
01996   // incremental display of content without causing an increase
01997   // in page load time. If this value is set below 1/10 of second
01998   // it starts to impact page load performance.
01999   // see bugzilla bug 72138 for more info.
02000   mNotificationInterval =
02001     nsContentUtils::GetIntPref("content.notify.interval", 120000);
02002 
02003   // The mMaxTokenProcessingTime controls how long we stay away from
02004   // the event loop when processing token. A lower value makes the app
02005   // more responsive, but may increase page load time.  The content
02006   // sink mNotificationInterval gates how frequently the content is
02007   // processed so it will also affect how interactive the app is
02008   // during page load also. The mNotification prevents contents
02009   // flushes from happening too frequently. while
02010   // mMaxTokenProcessingTime prevents flushes from happening too
02011   // infrequently.
02012 
02013   // The current ratio of 3 to 1 was determined to be the lowest
02014   // mMaxTokenProcessingTime which does not impact page load
02015   // performance.  See bugzilla bug 76722 for details.
02016 
02017   mMaxTokenProcessingTime =
02018     nsContentUtils::GetIntPref("content.max.tokenizing.time",
02019                                mNotificationInterval * 3);
02020 
02021   // 3/4 second (750000us) default for switching
02022   mDynamicIntervalSwitchThreshold =
02023     nsContentUtils::GetIntPref("content.switch.threshold", 750000);
02024 
02025   if (nsContentUtils::GetBoolPref("content.interrupt.parsing", PR_TRUE)) {
02026     mFlags |= NS_SINK_FLAG_CAN_INTERRUPT_PARSER;
02027   }
02028 
02029   // Changed from 8192 to greatly improve page loading performance on
02030   // large pages.  See bugzilla bug 77540.
02031   mMaxTextRun = nsContentUtils::GetIntPref("content.maxtextrun", 8191);
02032 
02033   nsCOMPtr<nsINodeInfo> nodeInfo;
02034   rv = mNodeInfoManager->GetNodeInfo(nsHTMLAtoms::html, nsnull,
02035                                      kNameSpaceID_None,
02036                                      getter_AddRefs(nodeInfo));
02037   NS_ENSURE_SUCCESS(rv, rv);
02038 
02039   // Make root part
02040   nsIContent *doc_root = mDocument->GetRootContent();
02041 
02042   if (doc_root) {
02043     // If the document already has a root we'll use it. This will
02044     // happen when we do document.open()/.write()/.close()...
02045 
02046     NS_ADDREF(mRoot = NS_STATIC_CAST(nsGenericHTMLElement*, doc_root));
02047   } else {
02048     mRoot = NS_NewHTMLHtmlElement(nodeInfo);
02049     if (!mRoot) {
02050       MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::Init()\n"));
02051       MOZ_TIMER_STOP(mWatch);
02052       return NS_ERROR_OUT_OF_MEMORY;
02053     }
02054     NS_ADDREF(mRoot);
02055 
02056     rv = mDocument->SetRootContent(mRoot);
02057     NS_ENSURE_SUCCESS(rv, rv);
02058   }
02059 
02060   // Make head part
02061   rv = mNodeInfoManager->GetNodeInfo(nsHTMLAtoms::head,
02062                                      nsnull, kNameSpaceID_None,
02063                                      getter_AddRefs(nodeInfo));
02064   NS_ENSURE_SUCCESS(rv, rv);
02065 
02066   mHead = NS_NewHTMLHeadElement(nodeInfo);
02067   if (NS_FAILED(rv)) {
02068     MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::Init()\n"));
02069     MOZ_TIMER_STOP(mWatch);
02070     return NS_ERROR_OUT_OF_MEMORY;
02071   }
02072   NS_ADDREF(mHead);
02073 
02074   mRoot->AppendChildTo(mHead, PR_FALSE);
02075 
02076   mCurrentContext = new SinkContext(this);
02077   NS_ENSURE_TRUE(mCurrentContext, NS_ERROR_OUT_OF_MEMORY);
02078   mCurrentContext->Begin(eHTMLTag_html, mRoot, 0, -1);
02079   mContextStack.AppendElement(mCurrentContext);
02080 
02081 #ifdef NS_DEBUG
02082   nsCAutoString spec;
02083   (void)aURI->GetSpec(spec);
02084   SINK_TRACE(SINK_TRACE_CALLS,
02085              ("HTMLContentSink::Init: this=%p url='%s'",
02086               this, spec.get()));
02087 #endif
02088 
02089   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::Init()\n"));
02090   MOZ_TIMER_STOP(mWatch);
02091 
02092   return NS_OK;
02093 }
02094 
02095 NS_IMETHODIMP
02096 HTMLContentSink::WillBuildModel(void)
02097 {
02098   if (mFlags & NS_SINK_FLAG_CAN_INTERRUPT_PARSER) {
02099     nsresult rv = AddDummyParserRequest();
02100     if (NS_FAILED(rv)) {
02101       NS_ERROR("Adding dummy parser request failed");
02102 
02103       // Don't return the error result, just reset flag which
02104       // indicates that it can interrupt parsing. If
02105       // AddDummyParserRequests fails it should not affect
02106       // WillBuildModel.
02107       mFlags &= ~NS_SINK_FLAG_CAN_INTERRUPT_PARSER;
02108     }
02109 
02110     mBeginLoadTime = PR_IntervalToMicroseconds(PR_IntervalNow());
02111   }
02112 
02113   mScrolledToRefAlready = PR_FALSE;
02114 
02115   if (mHTMLDocument) {
02116     NS_ASSERTION(mParser, "no parser");
02117     nsCompatibility mode = eCompatibility_NavQuirks;
02118     if (mParser) {
02119       nsDTDMode dtdMode = mParser->GetParseMode();
02120       switch (dtdMode) {
02121         case eDTDMode_full_standards:
02122           mode = eCompatibility_FullStandards;
02123 
02124           break;
02125         case eDTDMode_almost_standards:
02126           mode = eCompatibility_AlmostStandards;
02127 
02128           break;
02129         default:
02130           mode = eCompatibility_NavQuirks;
02131 
02132           break;
02133       }
02134     }
02135     mHTMLDocument->SetCompatibilityMode(mode);
02136   }
02137 
02138   // Notify document that the load is beginning
02139   mDocument->BeginLoad();
02140 
02141   return NS_OK;
02142 }
02143 
02144 NS_IMETHODIMP
02145 HTMLContentSink::DidBuildModel(void)
02146 {
02147 
02148   // NRA Dump stopwatch stop info here
02149 #ifdef MOZ_PERF_METRICS
02150   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::DidBuildModel(), this=%p\n",
02151                       this));
02152   MOZ_TIMER_STOP(mWatch);
02153   MOZ_TIMER_LOG(("Content creation time (this=%p): ", this));
02154   MOZ_TIMER_PRINT(mWatch);
02155 #endif
02156 
02157   // Cancel a timer if we had one out there
02158   if (mNotificationTimer) {
02159     SINK_TRACE(SINK_TRACE_REFLOW,
02160                ("HTMLContentSink::DidBuildModel: canceling notification "
02161                 "timeout"));
02162     mNotificationTimer->Cancel();
02163     mNotificationTimer = 0;
02164   }
02165 
02166   if (mDocument->GetDocumentTitle().IsVoid()) {
02167     nsCOMPtr<nsIDOMNSDocument> domDoc(do_QueryInterface(mDocument));
02168     domDoc->SetTitle(EmptyString());
02169   }
02170 
02171   // Reflow the last batch of content
02172   if (mBody || mFrameset) {
02173     SINK_TRACE(SINK_TRACE_REFLOW,
02174                ("HTMLContentSink::DidBuildModel: layout final content"));
02175     mCurrentContext->FlushTags(PR_TRUE);
02176   } else if (!mLayoutStarted) {
02177     // We never saw the body, and layout never got started. Force
02178     // layout *now*, to get an initial reflow.
02179     SINK_TRACE(SINK_TRACE_REFLOW,
02180                ("HTMLContentSink::DidBuildModel: forcing reflow on empty "
02181                 "document"));
02182 
02183     // NOTE: only force the layout if we are NOT destroying the
02184     // docshell. If we are destroying it, then starting layout will
02185     // likely cause us to crash, or at best waste a lot of time as we
02186     // are just going to tear it down anyway.
02187     PRBool bDestroying = PR_TRUE;
02188     if (mDocShell) {
02189       mDocShell->IsBeingDestroyed(&bDestroying);
02190     }
02191 
02192     if (!bDestroying) {
02193       StartLayout();
02194     }
02195   }
02196 
02197   if (mDocShell) {
02198     PRUint32 LoadType = 0;
02199     mDocShell->GetLoadType(&LoadType);
02200 
02201     if (ScrollToRef(!(LoadType & nsIDocShell::LOAD_CMD_HISTORY))) {
02202       mScrolledToRefAlready = PR_TRUE;
02203     }
02204   }
02205 
02206   nsIScriptLoader *loader = mDocument->GetScriptLoader();
02207   if (loader) {
02208     loader->RemoveObserver(this);
02209   }
02210 
02211   mDocument->EndLoad();
02212 
02213   // Ref. Bug 49115
02214   // Do this hack to make sure that the parser
02215   // doesn't get destroyed, accidently, before
02216   // the circularity, between sink & parser, is
02217   // actually borken.
02218   nsCOMPtr<nsIParser> kungFuDeathGrip(mParser);
02219 
02220   // Drop our reference to the parser to get rid of a circular
02221   // reference.
02222   mParser = nsnull;
02223 
02224   if (mFlags & NS_SINK_FLAG_DYNAMIC_LOWER_VALUE) {
02225     // Reset the performance hint which was set to FALSE
02226     // when NS_SINK_FLAG_DYNAMIC_LOWER_VALUE was set. 
02227     PL_FavorPerformanceHint(PR_TRUE , 0);
02228   }
02229 
02230   if (mFlags & NS_SINK_FLAG_CAN_INTERRUPT_PARSER) {
02231     // Note: Don't return value from RemoveDummyParserRequest,
02232     // If RemoveDummyParserRequests fails it should not affect
02233     // DidBuildModel. The remove can fail if the parser request
02234     // was already removed by a DummyParserRequest::Cancel
02235     RemoveDummyParserRequest();
02236   }
02237 
02238   return NS_OK;
02239 }
02240 
02241 NS_IMETHODIMP
02242 HTMLContentSink::Notify(nsITimer *timer)
02243 {
02244   MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::Notify()\n"));
02245   MOZ_TIMER_START(mWatch);
02246 
02247   if (mFlags & NS_SINK_FLAG_PARSING) {
02248     // We shouldn't interfere with our normal DidProcessAToken logic
02249     mFlags |= NS_SINK_FLAG_DROPPED_TIMER;
02250     return NS_OK;
02251   }
02252   
02253 #ifdef MOZ_DEBUG
02254   {
02255     PRTime now = PR_Now();
02256     PRInt64 diff, interval;
02257     PRInt32 delay;
02258 
02259     LL_I2L(interval, GetNotificationInterval());
02260     LL_SUB(diff, now, mLastNotificationTime);
02261 
02262     LL_SUB(diff, diff, interval);
02263     LL_L2I(delay, diff);
02264     delay /= PR_USEC_PER_MSEC;
02265 
02266     mBackoffCount--;
02267     SINK_TRACE(SINK_TRACE_REFLOW,
02268                ("HTMLContentSink::Notify: reflow on a timer: %d milliseconds "
02269                 "late, backoff count: %d", delay, mBackoffCount));
02270   }
02271 #endif
02272 
02273   if (mCurrentContext) {
02274     mCurrentContext->FlushTags(PR_TRUE);
02275   }
02276 
02277   // Now try and scroll to the reference
02278   // XXX Should we scroll unconditionally for history loads??
02279   TryToScrollToRef();
02280 
02281   mNotificationTimer = 0;
02282   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::Notify()\n"));
02283   MOZ_TIMER_STOP(mWatch);
02284   return NS_OK;
02285 }
02286 
02287 NS_IMETHODIMP
02288 HTMLContentSink::WillInterrupt()
02289 {
02290   nsresult result = NS_OK;
02291 
02292   SINK_TRACE(SINK_TRACE_CALLS, ("HTMLContentSink::WillInterrupt: this=%p",
02293                                 this));
02294 #ifndef SINK_NO_INCREMENTAL
02295   if (mNotifyOnTimer && mLayoutStarted) {
02296     if (mBackoffCount && !mInMonolithicContainer) {
02297       nsInt64 now(PR_Now());
02298       nsInt64 interval(GetNotificationInterval());
02299       nsInt64 lastNotification(mLastNotificationTime);
02300       nsInt64 diff(now - lastNotification);
02301 
02302       // If it's already time for us to have a notification
02303       if (diff > interval || (mFlags & NS_SINK_FLAG_DROPPED_TIMER)) {
02304         mBackoffCount--;
02305         SINK_TRACE(SINK_TRACE_REFLOW,
02306                  ("HTMLContentSink::WillInterrupt: flushing tags since we've "
02307                   "run out time; backoff count: %d", mBackoffCount));
02308         result = mCurrentContext->FlushTags(PR_TRUE);
02309         if (mFlags & NS_SINK_FLAG_DROPPED_TIMER) {
02310           TryToScrollToRef();
02311           mFlags &= ~NS_SINK_FLAG_DROPPED_TIMER;
02312         }
02313       } else if (!mNotificationTimer) {
02314         interval -= diff;
02315         PRInt32 delay = interval;
02316         
02317         // Convert to milliseconds
02318         delay /= PR_USEC_PER_MSEC;
02319 
02320         mNotificationTimer = do_CreateInstance("@mozilla.org/timer;1",
02321                                                &result);
02322         if (NS_SUCCEEDED(result)) {
02323           SINK_TRACE(SINK_TRACE_REFLOW,
02324                      ("HTMLContentSink::WillInterrupt: setting up timer with "
02325                       "delay %d", delay));
02326 
02327           result =
02328             mNotificationTimer->InitWithCallback(this, delay,
02329                                                  nsITimer::TYPE_ONE_SHOT);
02330           if (NS_FAILED(result)) {
02331             mNotificationTimer = nsnull;
02332           }
02333         }
02334       }
02335     }
02336   } else {
02337     SINK_TRACE(SINK_TRACE_REFLOW,
02338                ("HTMLContentSink::WillInterrupt: flushing tags "
02339                 "unconditionally"));
02340 
02341     result = mCurrentContext->FlushTags(PR_TRUE);
02342   }
02343 #endif
02344 
02345   mFlags &= ~NS_SINK_FLAG_PARSING;
02346   
02347   return result;
02348 }
02349 
02350 NS_IMETHODIMP
02351 HTMLContentSink::WillResume()
02352 {
02353   SINK_TRACE(SINK_TRACE_CALLS, ("HTMLContentSink::WillResume: this=%p", this));
02354 
02355   mFlags |= NS_SINK_FLAG_PARSING;
02356 
02357   return NS_OK;
02358 }
02359 
02360 NS_IMETHODIMP
02361 HTMLContentSink::SetParser(nsIParser* aParser)
02362 {
02363   mParser = aParser;
02364   return NS_OK;
02365 }
02366 
02367 NS_IMETHODIMP_(PRBool)
02368 HTMLContentSink::IsFormOnStack()
02369 {
02370   return mFlags & NS_SINK_FLAG_FORM_ON_STACK;
02371 }
02372 
02373 NS_IMETHODIMP
02374 HTMLContentSink::BeginContext(PRInt32 aPosition)
02375 {
02376   MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::BeginContext()\n"));
02377   MOZ_TIMER_START(mWatch);
02378   NS_PRECONDITION(aPosition > -1, "out of bounds");
02379 
02380   // Create new context
02381   SinkContext* sc = new SinkContext(this);
02382   if (!sc) {
02383     MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::BeginContext()\n"));
02384     MOZ_TIMER_STOP(mWatch);
02385 
02386     return NS_ERROR_OUT_OF_MEMORY;
02387   }
02388 
02389   if (!mCurrentContext) {
02390     NS_ERROR("Non-existing context");
02391 
02392     return NS_ERROR_FAILURE;
02393   }
02394 
02395   // Flush everything in the current context so that we don't have
02396   // to worry about insertions resulting in inconsistent frame creation.
02397   mCurrentContext->FlushTags(PR_TRUE);
02398 
02399   // Sanity check.
02400   if (mCurrentContext->mStackPos <= aPosition) {
02401     NS_ERROR("Out of bounds position");
02402     return NS_ERROR_FAILURE;
02403   }
02404 
02405   PRInt32 insertionPoint = -1;
02406   nsHTMLTag nodeType      = mCurrentContext->mStack[aPosition].mType;
02407   nsGenericHTMLElement* content = mCurrentContext->mStack[aPosition].mContent;
02408 
02409   // If the content under which the new context is created
02410   // has a child on the stack, the insertion point is
02411   // before the last child.
02412   if (aPosition < (mCurrentContext->mStackPos - 1)) {
02413     insertionPoint = content->GetChildCount() - 1;
02414   }
02415 
02416   sc->Begin(nodeType,
02417             content,
02418             mCurrentContext->mStack[aPosition].mNumFlushed,
02419             insertionPoint);
02420   NS_ADDREF(sc->mSink);
02421 
02422   mContextStack.AppendElement(mCurrentContext);
02423   mCurrentContext = sc;
02424 
02425   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::BeginContext()\n"));
02426   MOZ_TIMER_STOP(mWatch);
02427 
02428   return NS_OK;
02429 }
02430 
02431 NS_IMETHODIMP
02432 HTMLContentSink::EndContext(PRInt32 aPosition)
02433 {
02434   MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::EndContext()\n"));
02435   MOZ_TIMER_START(mWatch);
02436   NS_PRECONDITION(mCurrentContext && aPosition > -1, "non-existing context");
02437 
02438   PRInt32 n = mContextStack.Count() - 1;
02439   SinkContext* sc = (SinkContext*) mContextStack.ElementAt(n);
02440 
02441   NS_ASSERTION(sc->mStack[aPosition].mType == mCurrentContext->mStack[0].mType,
02442                "ending a wrong context");
02443 
02444   mCurrentContext->FlushTextAndRelease();
02445 
02446   sc->mStack[aPosition].mNumFlushed = mCurrentContext->mStack[0].mNumFlushed;
02447 
02448   for (PRInt32 i = 0; i<mCurrentContext->mStackPos; i++) {
02449     NS_IF_RELEASE(mCurrentContext->mStack[i].mContent);
02450   }
02451 
02452   delete [] mCurrentContext->mStack;
02453 
02454   mCurrentContext->mStack      = nsnull;
02455   mCurrentContext->mStackPos   = 0;
02456   mCurrentContext->mStackSize  = 0;
02457 
02458   delete [] mCurrentContext->mText;
02459 
02460   mCurrentContext->mText       = nsnull;
02461   mCurrentContext->mTextLength = 0;
02462   mCurrentContext->mTextSize   = 0;
02463 
02464   NS_IF_RELEASE(mCurrentContext->mSink);
02465 
02466   delete mCurrentContext;
02467 
02468   mCurrentContext = sc;
02469   mContextStack.RemoveElementAt(n);
02470 
02471   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::EndContext()\n"));
02472   MOZ_TIMER_STOP(mWatch);
02473 
02474   return NS_OK;
02475 }
02476 
02477 
02478 NS_IMETHODIMP
02479 HTMLContentSink::SetTitle(const nsString& aValue)
02480 {
02481   MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::SetTitle()\n"));
02482   MOZ_TIMER_START(mWatch);
02483 
02484   nsresult rv = OpenHeadContext();
02485   if (NS_SUCCEEDED(rv)) {
02486     rv = SetDocumentTitle(aValue, nsnull);
02487   }
02488   CloseHeadContext();
02489 
02490   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::SetTitle()\n"));
02491   MOZ_TIMER_STOP(mWatch);
02492 
02493   return rv;
02494 }
02495 
02496 NS_IMETHODIMP
02497 HTMLContentSink::OpenHTML(const nsIParserNode& aNode)
02498 {
02499   MOZ_TIMER_START(mWatch);
02500   SINK_TRACE_NODE(SINK_TRACE_CALLS,
02501                   "HTMLContentSink::OpenHTML", 
02502                   eHTMLTag_html, 0, this);
02503 
02504   if (mRoot) {
02505     // Add attributes to the node...if found.
02506     PRInt32 ac = aNode.GetAttributeCount();
02507 
02508     if (ac > 0) {
02509       AddAttributes(aNode, mRoot, PR_TRUE, PR_TRUE);
02510     }
02511   }
02512 
02513   MOZ_TIMER_STOP(mWatch);
02514 
02515   return NS_OK;
02516 }
02517 
02518 NS_IMETHODIMP
02519 HTMLContentSink::CloseHTML()
02520 {
02521   MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::CloseHTML()\n"));
02522   MOZ_TIMER_START(mWatch);
02523   SINK_TRACE_NODE(SINK_TRACE_CALLS,
02524                  "HTMLContentSink::CloseHTML", 
02525                  eHTMLTag_html, 0, this);
02526 
02527   if (mHeadContext) {
02528     if (mCurrentContext == mHeadContext) {
02529       PRInt32 numContexts = mContextStack.Count();
02530 
02531       // Pop off the second html context if it's not done earlier
02532       mCurrentContext = (SinkContext*)mContextStack.ElementAt(--numContexts);
02533       mContextStack.RemoveElementAt(numContexts);
02534     }
02535 
02536     mHeadContext->End();
02537 
02538     delete mHeadContext;
02539     mHeadContext = nsnull;
02540   }
02541 
02542   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::CloseHTML()\n"));
02543   MOZ_TIMER_STOP(mWatch);
02544 
02545   return NS_OK;
02546 }
02547 
02548 NS_IMETHODIMP
02549 HTMLContentSink::OpenHead(const nsIParserNode& aNode)
02550 {
02551   MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::OpenHead()\n"));
02552   MOZ_TIMER_START(mWatch);
02553   SINK_TRACE_NODE(SINK_TRACE_CALLS,
02554                   "HTMLContentSink::OpenHead", 
02555                   eHTMLTag_head, 0, this);
02556 
02557   nsresult rv = OpenHeadContext();
02558   if (NS_FAILED(rv)) {
02559     MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::OpenHead()\n"));
02560     MOZ_TIMER_STOP(mWatch);
02561     return rv;
02562   }
02563 
02564   if (mHead && aNode.GetNodeType() == eHTMLTag_head) {
02565     rv = AddAttributes(aNode, mHead, PR_FALSE, PR_TRUE);
02566   }
02567 
02568   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::OpenHead()\n"));
02569   MOZ_TIMER_STOP(mWatch);
02570 
02571   return rv;
02572 }
02573 
02574 NS_IMETHODIMP
02575 HTMLContentSink::CloseHead()
02576 {
02577   MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::CloseHead()\n"));
02578   MOZ_TIMER_START(mWatch);
02579   SINK_TRACE_NODE(SINK_TRACE_CALLS,
02580                   "HTMLContentSink::CloseHead", 
02581                   eHTMLTag_head, 0, this);
02582   CloseHeadContext();
02583 
02584   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::CloseHead()\n"));
02585   MOZ_TIMER_STOP(mWatch);
02586 
02587   return NS_OK;
02588 }
02589 
02590 NS_IMETHODIMP
02591 HTMLContentSink::OpenBody(const nsIParserNode& aNode)
02592 {
02593   MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::OpenBody()\n"));
02594   MOZ_TIMER_START(mWatch);
02595 
02596   SINK_TRACE_NODE(SINK_TRACE_CALLS,
02597                   "HTMLContentSink::OpenBody", 
02598                   eHTMLTag_body,
02599                   mCurrentContext->mStackPos, 
02600                   this);
02601 
02602   CloseHeadContext();  // do this just in case if the HEAD was left open!
02603 
02604   // Add attributes, if any, to the current BODY node
02605   if (mBody) {
02606     AddAttributes(aNode, mBody, PR_TRUE, PR_TRUE);
02607 
02608     MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::OpenBody()\n"));
02609     MOZ_TIMER_STOP(mWatch);
02610 
02611     return NS_OK;
02612   }
02613 
02614   nsresult rv = mCurrentContext->OpenContainer(aNode);
02615 
02616   if (NS_FAILED(rv)) {
02617     MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::OpenBody()\n"));
02618     MOZ_TIMER_STOP(mWatch);
02619 
02620     return rv;
02621   }
02622 
02623   mBody = mCurrentContext->mStack[mCurrentContext->mStackPos - 1].mContent;
02624 
02625   NS_ADDREF(mBody);
02626 
02627   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::OpenBody()\n"));
02628   MOZ_TIMER_STOP(mWatch);
02629 
02630   if (mCurrentContext->mStackPos > 1) {
02631     PRInt32 parentIndex    = mCurrentContext->mStackPos - 2;
02632     nsGenericHTMLElement *parent = mCurrentContext->mStack[parentIndex].mContent;
02633     PRInt32 numFlushed     = mCurrentContext->mStack[parentIndex].mNumFlushed;
02634     PRInt32 insertionPoint =
02635       mCurrentContext->mStack[parentIndex].mInsertionPoint;
02636 
02637     // XXX: I have yet to see a case where numFlushed is non-zero and
02638     // insertionPoint is not -1, but this code will try to handle
02639     // those cases too.
02640 
02641     if (insertionPoint != -1) {
02642       NotifyInsert(parent, mBody, insertionPoint - 1);
02643     } else {
02644       NotifyAppend(parent, numFlushed);
02645     }
02646   }
02647 
02648   StartLayout();
02649 
02650   return NS_OK;
02651 }
02652 
02653 NS_IMETHODIMP
02654 HTMLContentSink::CloseBody()
02655 {
02656   MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::CloseBody()\n"));
02657   MOZ_TIMER_START(mWatch);
02658   SINK_TRACE_NODE(SINK_TRACE_CALLS,
02659                   "HTMLContentSink::CloseBody", 
02660                   eHTMLTag_body,
02661                   mCurrentContext->mStackPos - 1, 
02662                   this);
02663 
02664   PRBool didFlush;
02665   nsresult rv = mCurrentContext->FlushTextAndRelease(&didFlush);
02666   if (NS_FAILED(rv)) {
02667     MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::CloseBody()\n"));
02668     MOZ_TIMER_STOP(mWatch);
02669 
02670     return rv;
02671   }
02672 
02673   // Flush out anything that's left
02674   SINK_TRACE(SINK_TRACE_REFLOW,
02675              ("HTMLContentSink::CloseBody: layout final body content"));
02676 
02677   mCurrentContext->FlushTags(PR_TRUE);
02678   mCurrentContext->CloseContainer(eHTMLTag_body);
02679 
02680   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::CloseBody()\n"));
02681   MOZ_TIMER_STOP(mWatch);
02682 
02683   return NS_OK;
02684 }
02685 
02686 NS_IMETHODIMP
02687 HTMLContentSink::OpenForm(const nsIParserNode& aNode)
02688 {
02689   MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::OpenForm()\n"));
02690   MOZ_TIMER_START(mWatch);
02691 
02692   nsresult result = NS_OK;
02693 
02694   mCurrentContext->FlushTextAndRelease();
02695 
02696   SINK_TRACE_NODE(SINK_TRACE_CALLS,
02697                   "HTMLContentSink::OpenForm", 
02698                   eHTMLTag_form,
02699                   mCurrentContext->mStackPos, 
02700                   this);
02701 
02702   // Close out previous form if it's there. If there is one
02703   // around, it's probably because the last one wasn't well-formed.
02704   mCurrentForm = nsnull;
02705 
02706   // Check if the parent is a table, tbody, thead, tfoot, tr, col or
02707   // colgroup. If so, we fix up by making the form leaf content.
02708   if (mCurrentContext->IsCurrentContainer(eHTMLTag_table) ||
02709       mCurrentContext->IsCurrentContainer(eHTMLTag_tbody) ||
02710       mCurrentContext->IsCurrentContainer(eHTMLTag_thead) ||
02711       mCurrentContext->IsCurrentContainer(eHTMLTag_tfoot) ||
02712       mCurrentContext->IsCurrentContainer(eHTMLTag_tr) ||
02713       mCurrentContext->IsCurrentContainer(eHTMLTag_col) ||
02714       mCurrentContext->IsCurrentContainer(eHTMLTag_colgroup)) {
02715     nsCOMPtr<nsINodeInfo> nodeInfo;
02716     result = mNodeInfoManager->GetNodeInfo(nsHTMLAtoms::form, nsnull,
02717                                            kNameSpaceID_None,
02718                                            getter_AddRefs(nodeInfo));
02719     NS_ENSURE_SUCCESS(result, result);
02720 
02721     mCurrentForm = NS_NewHTMLFormElement(nodeInfo);
02722     if (!mCurrentForm) {
02723       return NS_ERROR_OUT_OF_MEMORY;
02724     }
02725 
02726     result = AddLeaf(aNode);
02727   } else {
02728     mFlags |= NS_SINK_FLAG_FORM_ON_STACK;
02729     // Otherwise the form can be a content parent.
02730     result = mCurrentContext->OpenContainer(aNode);
02731     if (NS_SUCCEEDED(result)) {
02732       mCurrentForm = dont_AddRef(mCurrentContext->GetCurrentContainer());
02733     }
02734   }
02735 
02736   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::OpenForm()\n"));
02737   MOZ_TIMER_STOP(mWatch);
02738 
02739   return result;
02740 }
02741 
02742 // XXX MAYBE add code to place close form tag into the content model
02743 // for navigator layout compatability.
02744 NS_IMETHODIMP
02745 HTMLContentSink::CloseForm()
02746 {
02747   MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::CloseForm()\n"));
02748   MOZ_TIMER_START(mWatch);
02749 
02750   nsresult result = NS_OK;
02751 
02752   SINK_TRACE_NODE(SINK_TRACE_CALLS,
02753                   "HTMLContentSink::CloseForm",
02754                   eHTMLTag_form,
02755                   mCurrentContext->mStackPos - 1, 
02756                   this);
02757 
02758   if (mCurrentForm) {
02759     // if this is a well-formed form, close it too
02760     if (mCurrentContext->IsCurrentContainer(eHTMLTag_form)) {
02761       mCurrentContext->FlushTextAndRelease();
02762       result = mCurrentContext->CloseContainer(eHTMLTag_form);
02763       mFlags &= ~NS_SINK_FLAG_FORM_ON_STACK;
02764     }
02765 
02766     mCurrentForm = nsnull;
02767   }
02768 
02769   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::CloseForm()\n"));
02770   MOZ_TIMER_STOP(mWatch);
02771 
02772   return result;
02773 }
02774 
02775 NS_IMETHODIMP
02776 HTMLContentSink::OpenFrameset(const nsIParserNode& aNode)
02777 {
02778   MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::OpenFrameset()\n"));
02779   MOZ_TIMER_START(mWatch);
02780   SINK_TRACE_NODE(SINK_TRACE_CALLS,
02781                   "HTMLContentSink::OpenFrameset", 
02782                   eHTMLTag_frameset,
02783                   mCurrentContext->mStackPos, 
02784                   this);
02785 
02786   CloseHeadContext(); // do this just in case if the HEAD was left open!
02787 
02788   nsresult rv = mCurrentContext->OpenContainer(aNode);
02789   PRBool isFirstFrameset = PR_FALSE;
02790   if (NS_SUCCEEDED(rv) && !mFrameset &&
02791       (mFlags & NS_SINK_FLAG_FRAMES_ENABLED)) {
02792     mFrameset =
02793       mCurrentContext->mStack[mCurrentContext->mStackPos - 1].mContent;
02794     NS_ADDREF(mFrameset);
02795     isFirstFrameset = PR_TRUE;
02796   }
02797 
02798   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::OpenFrameset()\n"));
02799   MOZ_TIMER_STOP(mWatch);
02800 
02801   if (isFirstFrameset && mCurrentContext->mStackPos > 1) {
02802     NS_ASSERTION(mFrameset, "Must have frameset!");
02803     // Have to notify for the frameset now, since we never actually
02804     // close out <html>, so won't notify for it then.
02805     PRInt32 parentIndex    = mCurrentContext->mStackPos - 2;
02806     nsGenericHTMLElement *parent = mCurrentContext->mStack[parentIndex].mContent;
02807     PRInt32 numFlushed     = mCurrentContext->mStack[parentIndex].mNumFlushed;
02808     PRInt32 insertionPoint =
02809       mCurrentContext->mStack[parentIndex].mInsertionPoint;
02810 
02811     // XXX: I have yet to see a case where numFlushed is non-zero and
02812     // insertionPoint is not -1, but this code will try to handle
02813     // those cases too.
02814 
02815     if (insertionPoint != -1) {
02816       NotifyInsert(parent, mFrameset, insertionPoint - 1);
02817     } else {
02818       NotifyAppend(parent, numFlushed);
02819     }
02820   }
02821   
02822   return rv;
02823 }
02824 
02825 NS_IMETHODIMP
02826 HTMLContentSink::CloseFrameset()
02827 {
02828   MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::CloseFrameset()\n"));
02829   MOZ_TIMER_START(mWatch);
02830   SINK_TRACE_NODE(SINK_TRACE_CALLS,
02831                    "HTMLContentSink::CloseFrameset", 
02832                    eHTMLTag_frameset,
02833                    mCurrentContext->mStackPos - 1,
02834                    this);
02835 
02836   SinkContext* sc = mCurrentContext;
02837   nsGenericHTMLElement* fs = sc->mStack[sc->mStackPos - 1].mContent;
02838   PRBool done = fs == mFrameset;
02839 
02840   nsresult rv;
02841   if (done) {
02842     PRBool didFlush;
02843     rv = sc->FlushTextAndRelease(&didFlush);
02844     if (NS_FAILED(rv)) {
02845       MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::CloseFrameset()\n"));
02846       MOZ_TIMER_STOP(mWatch);
02847 
02848       return rv;
02849     }
02850 
02851     // Flush out anything that's left
02852     SINK_TRACE(SINK_TRACE_REFLOW,
02853                ("HTMLContentSink::CloseFrameset: layout final content"));
02854 
02855     sc->FlushTags(PR_TRUE);
02856   }
02857 
02858   rv = sc->CloseContainer(eHTMLTag_frameset);    
02859 
02860   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::CloseFrameset()\n"));
02861   MOZ_TIMER_STOP(mWatch);
02862 
02863   if (done && (mFlags & NS_SINK_FLAG_FRAMES_ENABLED)) {
02864     StartLayout();
02865   }
02866 
02867   return rv;
02868 }
02869 
02870 NS_IMETHODIMP
02871 HTMLContentSink::OpenMap(const nsIParserNode& aNode)
02872 {
02873   MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::OpenMap()\n"));
02874   MOZ_TIMER_START(mWatch);
02875 
02876   nsresult rv = NS_OK;
02877 
02878   SINK_TRACE_NODE(SINK_TRACE_CALLS,
02879                   "HTMLContentSink::OpenMap", 
02880                   eHTMLTag_map,
02881                   mCurrentContext->mStackPos, 
02882                   this);
02883 
02884   // We used to treat MAP elements specially (i.e. they were
02885   // only parent elements for AREAs), but we don't anymore.
02886   // HTML 4.0 says that MAP elements can have block content
02887   // as children.
02888   rv = mCurrentContext->OpenContainer(aNode);
02889 
02890   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::OpenMap()\n"));
02891   MOZ_TIMER_STOP(mWatch);
02892 
02893   return rv;
02894 }
02895 
02896 NS_IMETHODIMP
02897 HTMLContentSink::CloseMap()
02898 {
02899   MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::CloseMap()\n"));
02900   MOZ_TIMER_START(mWatch);
02901 
02902   nsresult rv = NS_OK;
02903 
02904   SINK_TRACE_NODE(SINK_TRACE_CALLS,
02905                   "HTMLContentSink::CloseMap", 
02906                   eHTMLTag_map,
02907                   mCurrentContext->mStackPos - 1, 
02908                   this);
02909 
02910   mCurrentMap = nsnull;
02911 
02912   rv = mCurrentContext->CloseContainer(eHTMLTag_map);
02913 
02914   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::CloseMap()\n"));
02915   MOZ_TIMER_STOP(mWatch);
02916 
02917   return rv;
02918 }
02919 
02920 NS_IMETHODIMP
02921 HTMLContentSink::IsEnabled(PRInt32 aTag, PRBool* aReturn)
02922 {
02923   nsHTMLTag theHTMLTag = nsHTMLTag(aTag);
02924 
02925   if (theHTMLTag == eHTMLTag_script) {
02926     *aReturn = mFlags & NS_SINK_FLAG_SCRIPT_ENABLED ? PR_TRUE : PR_FALSE;
02927   } else if (theHTMLTag == eHTMLTag_frameset) {
02928     *aReturn = mFlags & NS_SINK_FLAG_FRAMES_ENABLED ? PR_TRUE : PR_FALSE;
02929   } else {
02930     *aReturn = PR_FALSE;
02931   }
02932 
02933   return NS_OK;
02934 }
02935 
02936 NS_IMETHODIMP
02937 HTMLContentSink::OpenContainer(const nsIParserNode& aNode)
02938 {
02939   MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::OpenContainer()\n"));
02940   MOZ_TIMER_START(mWatch);
02941 
02942   nsresult rv = NS_OK;
02943 
02944   // XXX work around parser bug
02945   if (eHTMLTag_frameset == aNode.GetNodeType()) {
02946     rv = OpenFrameset(aNode);
02947   } else {
02948     rv = mCurrentContext->OpenContainer(aNode);
02949   }
02950 
02951   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::OpenContainer()\n"));
02952   MOZ_TIMER_STOP(mWatch);
02953 
02954   return rv;
02955 }
02956 
02957 NS_IMETHODIMP
02958 HTMLContentSink::CloseContainer(const eHTMLTags aTag)
02959 {
02960   MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::CloseContainer()\n"));
02961   MOZ_TIMER_START(mWatch);
02962 
02963   // XXX work around parser bug
02964   if (eHTMLTag_frameset == aTag) {
02965     MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::CloseContainer()\n"));
02966     MOZ_TIMER_STOP(mWatch);
02967     return CloseFrameset();
02968   }
02969 
02970   nsresult rv = mCurrentContext->CloseContainer(aTag);
02971 
02972   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::CloseContainer()\n"));
02973   MOZ_TIMER_STOP(mWatch);
02974 
02975   return rv;
02976 }
02977 
02978 NS_IMETHODIMP
02979 HTMLContentSink::AddHeadContent(const nsIParserNode& aNode)
02980 {
02981   MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::AddHeadContent()\n"));
02982   MOZ_TIMER_START(mWatch);
02983 
02984   nsresult rv = OpenHeadContext();
02985   if (NS_SUCCEEDED(rv)) {
02986     nsHTMLTag type = nsHTMLTag(aNode.GetNodeType());
02987     if (eHTMLTag_title == type) {
02988       nsCOMPtr<nsIDTD> dtd;
02989       mParser->GetDTD(getter_AddRefs(dtd));
02990       if (dtd) {
02991         nsAutoString title;
02992         PRInt32 lineNo = 0;
02993         dtd->CollectSkippedContent(eHTMLTag_title, title, lineNo);
02994         rv = SetDocumentTitle(title, &aNode);
02995       }
02996     }
02997     else {
02998       rv = AddLeaf(aNode);
02999     }
03000     CloseHeadContext();
03001   }
03002    
03003   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::AddHeadContent()\n"));
03004   MOZ_TIMER_STOP(mWatch);
03005   return rv;
03006 }
03007 
03008 
03009 NS_IMETHODIMP
03010 HTMLContentSink::AddLeaf(const nsIParserNode& aNode)
03011 {
03012   MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::AddLeaf()\n"));
03013   MOZ_TIMER_START(mWatch);
03014 
03015   nsresult rv;
03016 
03017   nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
03018   switch (nodeType) {
03019   case eHTMLTag_area:
03020     rv = ProcessAREATag(aNode);
03021 
03022     break;
03023   case eHTMLTag_base:
03024     mCurrentContext->FlushTextAndRelease();
03025     rv = ProcessBASETag(aNode);
03026 
03027     break;
03028   case eHTMLTag_link:
03029     mCurrentContext->FlushTextAndRelease();
03030     rv = ProcessLINKTag(aNode);
03031 
03032     break;
03033   case eHTMLTag_meta:
03034     mCurrentContext->FlushTextAndRelease();
03035     rv = ProcessMETATag(aNode);
03036 
03037     break;
03038   case eHTMLTag_style:
03039     mCurrentContext->FlushTextAndRelease();
03040     rv = ProcessSTYLETag(aNode);
03041 
03042     break;
03043   case eHTMLTag_script:
03044     mCurrentContext->FlushTextAndRelease();
03045     rv = ProcessSCRIPTTag(aNode);
03046 
03047     break;
03048   default:
03049     rv = mCurrentContext->AddLeaf(aNode);
03050 
03051     break;
03052   }
03053 
03054   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::AddLeaf()\n"));
03055   MOZ_TIMER_STOP(mWatch);
03056 
03057   return rv;
03058 }
03059 
03060 nsresult 
03061 HTMLContentSink::SetDocumentTitle(const nsAString& aTitle, const nsIParserNode* aNode)
03062 {
03063   MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::SetDocumentTitle()\n"));
03064   MOZ_TIMER_START(mWatch);
03065   NS_ASSERTION(mCurrentContext == mHeadContext, "title not in head");
03066 
03067   if (!mDocument->GetDocumentTitle().IsVoid()) {
03068     // If the title was already set then don't try to overwrite it
03069     // when a new title is encountered - For backwards compatiblity
03070     MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::SetDocumentTitle()\n"));
03071     MOZ_TIMER_STOP(mWatch);
03072 
03073     return NS_OK;
03074   }
03075 
03076   nsAutoString title(aTitle);
03077   title.CompressWhitespace(PR_TRUE, PR_TRUE);
03078 
03079   nsCOMPtr<nsIDOMNSDocument> domDoc(do_QueryInterface(mDocument));
03080   domDoc->SetTitle(title);
03081 
03082   nsCOMPtr<nsINodeInfo> nodeInfo;
03083   nsresult rv = mNodeInfoManager->GetNodeInfo(nsHTMLAtoms::title, nsnull,
03084                                               kNameSpaceID_None,
03085                                               getter_AddRefs(nodeInfo));
03086   NS_ENSURE_SUCCESS(rv, rv);
03087 
03088   nsRefPtr<nsGenericHTMLElement> it = NS_NewHTMLTitleElement(nodeInfo);
03089   if (!it) {
03090     return NS_ERROR_OUT_OF_MEMORY;
03091   }
03092 
03093   if (aNode) {
03094     AddAttributes(*aNode, it);
03095   }
03096 
03097   nsCOMPtr<nsITextContent> text;
03098   rv = NS_NewTextNode(getter_AddRefs(text), mNodeInfoManager);
03099   NS_ENSURE_SUCCESS(rv, rv);
03100 
03101   text->SetText(title, PR_TRUE);
03102 
03103   it->AppendChildTo(text, PR_FALSE);
03104 
03105   mHead->AppendChildTo(it, PR_FALSE);
03106 
03107   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::SetDocumentTitle()\n"));
03108   MOZ_TIMER_STOP(mWatch);
03109 
03110   return NS_OK;
03111 }
03112 
03119 nsresult
03120 HTMLContentSink::AddComment(const nsIParserNode& aNode)
03121 {
03122   MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::AddComment()\n"));
03123   MOZ_TIMER_START(mWatch);
03124 
03125   nsresult rv = mCurrentContext->AddComment(aNode);
03126 
03127   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::AddComment()\n"));
03128   MOZ_TIMER_STOP(mWatch);
03129 
03130   return rv;
03131 }
03132 
03139 nsresult
03140 HTMLContentSink::AddProcessingInstruction(const nsIParserNode& aNode)
03141 {
03142   nsresult result = NS_OK;
03143 
03144   MOZ_TIMER_START(mWatch);
03145   // Implementation of AddProcessingInstruction() should start here
03146 
03147   MOZ_TIMER_STOP(mWatch);
03148 
03149   return result;
03150 }
03151 
03157 NS_IMETHODIMP
03158 HTMLContentSink::AddDocTypeDecl(const nsIParserNode& aNode)
03159 {
03160   MOZ_TIMER_DEBUGLOG(("Start: nsHTMLContentSink::AddDocTypeDecl()\n"));
03161   MOZ_TIMER_START(mWatch);
03162 
03163   nsCOMPtr<nsIDOMDocument> doc(do_QueryInterface(mHTMLDocument));
03164 
03165   if (!doc) {
03166     return NS_OK;
03167   }
03168 
03169   nsAutoString docTypeStr(aNode.GetText());
03170   nsresult rv = NS_OK;
03171 
03172   PRInt32 publicStart = docTypeStr.Find("PUBLIC", PR_TRUE);
03173   PRInt32 systemStart = docTypeStr.Find("SYSTEM", PR_TRUE);
03174   nsAutoString name, publicId, systemId;
03175 
03176   if (publicStart >= 0 || systemStart >= 0) {
03177     /*
03178      * If we find the keyword 'PUBLIC' after the keyword 'SYSTEM' we assume
03179      * that we got a system id that happens to contain the string "PUBLIC"
03180      * and we ignore that as the start of the public id.
03181      */
03182     if (systemStart >= 0 && (publicStart > systemStart)) {
03183       publicStart = -1;
03184     }
03185 
03186     /*
03187      * We found 'PUBLIC' or 'SYSTEM' in the doctype, put everything before
03188      * the first one of those in name.
03189      */
03190     docTypeStr.Mid(name, 0, publicStart >= 0 ? publicStart : systemStart);
03191 
03192     if (publicStart >= 0) {
03193       // We did find 'PUBLIC'
03194       docTypeStr.Mid(publicId, publicStart + 6,
03195                      docTypeStr.Length() - publicStart);
03196       publicId.Trim(" \t\n\r");
03197 
03198       // Strip quotes
03199       PRUnichar ch = publicId.IsEmpty() ? '\0' : publicId.First();
03200 
03201       PRBool hasQuote = PR_FALSE;
03202       if (ch == '"' || ch == '\'') {
03203         publicId.Cut(0, 1);
03204 
03205         PRInt32 end = publicId.FindChar(ch);
03206 
03207         if (end < 0) {
03208           /*
03209            * We didn't find an end quote, so just make sure we cut off
03210            * the '>' on the end of the doctype declaration
03211            */
03212 
03213           end = publicId.FindChar('>');
03214         } else {
03215           hasQuote = PR_TRUE;
03216         }
03217 
03218         /*
03219          * If we didn't find a closing quote or a '>' we leave publicId as
03220          * it is.
03221          */
03222         if (end >= 0) {
03223           publicId.Truncate(end);
03224         }
03225       } else {
03226         // No quotes, ignore the public id
03227         publicId.Truncate();
03228       }
03229 
03230       /*
03231        * Make sure the 'SYSTEM' word we found is not inside the pubilc id
03232        */
03233       PRInt32 pos = docTypeStr.Find(publicId);
03234 
03235       if (systemStart > 0) {
03236         if (systemStart < pos + (PRInt32)publicId.Length()) {
03237           systemStart = docTypeStr.Find("SYSTEM", PR_TRUE,
03238                                         pos + publicId.Length());
03239         }
03240       }
03241 
03242       /*
03243        * If we didn't find 'SYSTEM' we treat everything after the public id
03244        * as the system id.
03245        */
03246       if (systemStart < 0) {
03247         // 1 is the end quote
03248         systemStart = pos + publicId.Length() + (hasQuote ? 1 : 0);
03249       }
03250     }
03251 
03252     if (systemStart >= 0) {
03253       // We either found 'SYSTEM' or we have data after the public id
03254       docTypeStr.Mid(systemId, systemStart,
03255                      docTypeStr.Length() - systemStart);
03256 
03257       // Strip off 'SYSTEM' if we have it.
03258       if (StringBeginsWith(systemId, NS_LITERAL_STRING("SYSTEM")))
03259         systemId.Cut(0, 6);
03260 
03261       systemId.Trim(" \t\n\r");
03262 
03263       // Strip quotes
03264       PRUnichar ch = systemId.IsEmpty() ? '\0' : systemId.First();
03265 
03266       if (ch == '"' || ch == '\'') {
03267         systemId.Cut(0, 1);
03268 
03269         PRInt32 end = systemId.FindChar(ch);
03270 
03271         if (end < 0) {
03272           // We didn't find an end quote, then we just make sure we
03273           // cut of the '>' on the end of the doctype declaration
03274 
03275           end = systemId.FindChar('>');
03276         }
03277 
03278         // If we found an closing quote nor a '>' we truncate systemId
03279         // at that length.
03280         if (end >= 0) {
03281           systemId.Truncate(end);
03282         }
03283       } else {
03284         systemId.Truncate();
03285       }
03286     }
03287   } else {
03288     name.Assign(docTypeStr);
03289   }
03290 
03291   // Cut out "<!DOCTYPE" or "DOCTYPE" from the name.
03292   if (StringBeginsWith(name, NS_LITERAL_STRING("<!DOCTYPE"), nsCaseInsensitiveStringComparator())) {
03293     name.Cut(0, 9);
03294   } else if (StringBeginsWith(name, NS_LITERAL_STRING("DOCTYPE"), nsCaseInsensitiveStringComparator())) {
03295     name.Cut(0, 7);
03296   }
03297 
03298   name.Trim(" \t\n\r");
03299 
03300   // Check if name contains whitespace chars. If it does and the first
03301   // char is not a quote, we set the name to contain the characters
03302   // before the whitespace
03303   PRInt32 nameEnd = 0;
03304 
03305   if (name.IsEmpty() || (name.First() != '"' && name.First() != '\'')) {
03306     nameEnd = name.FindCharInSet(" \n\r\t");
03307   }
03308 
03309   // If we didn't find a public id we grab everything after the name
03310   // and use that as public id.
03311   if (publicStart < 0) {
03312     name.Mid(publicId, nameEnd, name.Length() - nameEnd);
03313     publicId.Trim(" \t\n\r");
03314 
03315     PRUnichar ch = publicId.IsEmpty() ? '\0' : publicId.First();
03316 
03317     if (ch == '"' || ch == '\'') {
03318       publicId.Cut(0, 1);
03319 
03320       PRInt32 publicEnd = publicId.FindChar(ch);
03321 
03322       if (publicEnd < 0) {
03323         publicEnd = publicId.FindChar('>');
03324       }
03325 
03326       if (publicEnd < 0) {
03327         publicEnd = publicId.Length();
03328       }
03329 
03330       publicId.Truncate(publicEnd);
03331     } else {
03332       // No quotes, no public id
03333       publicId.Truncate();
03334     }
03335   }
03336 
03337   if (nameEnd >= 0) {
03338     name.Truncate(nameEnd);
03339   } else {
03340     nameEnd = name.FindChar('>');
03341 
03342     if (nameEnd >= 0) {
03343       name.Truncate(nameEnd);
03344     }
03345   }
03346 
03347   if (!publicId.IsEmpty() || !systemId.IsEmpty() || !name.IsEmpty()) {
03348     nsCOMPtr<nsIDOMDocumentType> oldDocType;
03349     nsCOMPtr<nsIDOMDocumentType> docType;
03350 
03351     doc->GetDoctype(getter_AddRefs(oldDocType));
03352 
03353     nsCOMPtr<nsIDOMDOMImplementation> domImpl;
03354 
03355     rv = doc->GetImplementation(getter_AddRefs(domImpl));
03356 
03357     if (NS_FAILED(rv) || !domImpl) {
03358       return rv;
03359     }
03360 
03361     if (name.IsEmpty()) {
03362       name.AssignLiteral("HTML");
03363     }
03364 
03365     rv = domImpl->CreateDocumentType(name, publicId, systemId,
03366                                      getter_AddRefs(docType));
03367 
03368     if (NS_FAILED(rv) || !docType) {
03369       return rv;
03370     }
03371     nsCOMPtr<nsIDOMNode> tmpNode;
03372 
03373     if (oldDocType) {
03374       // If we already have a doctype we replace the old one.
03375 
03376       rv = doc->ReplaceChild(oldDocType, docType, getter_AddRefs(tmpNode));
03377     } else {
03378       // If we don't already have one we insert it as the first child,
03379       // this might not be 100% correct but since this is called from
03380       // the content sink we assume that this is what we want.
03381       nsCOMPtr<nsIDOMNode> firstChild;
03382 
03383       doc->GetFirstChild(getter_AddRefs(firstChild));
03384 
03385       // If the above fails it must be because we don't have any child
03386       // nodes, then firstChild will be 0 and InsertBefore() will
03387       // append
03388 
03389       rv = doc->InsertBefore(docType, firstChild, getter_AddRefs(tmpNode));
03390     }
03391   }
03392 
03393   MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::AddDocTypeDecl()\n"));
03394   MOZ_TIMER_STOP(mWatch);
03395 
03396   return rv;
03397 }
03398 
03399 
03400 NS_IMETHODIMP
03401 HTMLContentSink::WillProcessTokens(void)
03402 {
03403   if (mFlags & NS_SINK_FLAG_CAN_INTERRUPT_PARSER) {
03404     mDelayTimerStart = PR_IntervalToMicroseconds(PR_IntervalNow());
03405   }
03406 
03407   return NS_OK;
03408 }
03409 
03410 NS_IMETHODIMP
03411 HTMLContentSink::DidProcessTokens(void)
03412 {
03413   return NS_OK;
03414 }
03415 
03416 NS_IMETHODIMP
03417 HTMLContentSink::WillProcessAToken(void)
03418 {
03419   return NS_OK;
03420 }
03421 
03422 NS_IMETHODIMP
03423 HTMLContentSink::DidProcessAToken(void)
03424 {
03425   if (mFlags & NS_SINK_FLAG_CAN_INTERRUPT_PARSER) {
03426     // There is both a high frequency interrupt mode and a low
03427     // frequency interupt mode controlled by the flag
03428     // NS_SINK_FLAG_DYNAMIC_LOWER_VALUE The high frequency mode
03429     // interupts the parser frequently to provide UI responsiveness at
03430     // the expense of page load time. The low frequency mode
03431     // interrupts the parser and samples the system clock infrequently
03432     // to provide fast page load time. When the user moves the mouse,
03433     // clicks or types the mode switches to the high frequency
03434     // interrupt mode. If the user stops moving the mouse or typing
03435     // for a duration of time (mDynamicIntervalSwitchThreshold) it
03436     // switches to low frequency interrupt mode.
03437 
03438     // Get the current user event time
03439     nsIPresShell *shell = mDocument->GetShellAt(0);
03440 
03441     if (!shell) {
03442       // If there's no pres shell in the document, return early since
03443       // we're not laying anything out here.
03444 
03445       return NS_OK;
03446     }
03447 
03448     nsIViewManager* vm = shell->GetViewManager();
03449     NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
03450     PRUint32 eventTime;
03451     nsCOMPtr<nsIWidget> widget;
03452     nsresult rv = vm->GetWidget(getter_AddRefs(widget));
03453     if (!widget || NS_FAILED(widget->GetLastInputEventTime(eventTime))) {
03454         // If we can't get the last input time from the widget
03455         // then we will get it from the viewmanager.
03456         rv = vm->GetLastUserEventTime(eventTime);
03457         NS_ENSURE_SUCCESS(rv , NS_ERROR_FAILURE);
03458     }
03459 
03460     NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
03461 
03462     if ((!(mFlags & NS_SINK_FLAG_DYNAMIC_LOWER_VALUE)) &&
03463       (mLastSampledUserEventTime == eventTime)) {
03464       // The magic value of NS_MAX_TOKENS_DEFLECTED_IN_LOW_FREQ_MODE
03465       // was selected by empirical testing. It provides reasonable
03466       // user response and prevents us from sampling the clock too
03467       // frequently.
03468       if (mDeflectedCount < NS_MAX_TOKENS_DEFLECTED_IN_LOW_FREQ_MODE) {
03469         mDeflectedCount++;
03470 
03471         // return early to prevent sampling the clock. Note: This
03472         // prevents us from switching to higher frequency (better UI
03473         // responsive) mode, so limit ourselves to doing for no more
03474         // than NS_MAX_TOKENS_DEFLECTED_IN_LOW_FREQ_MODE tokens.
03475 
03476         return NS_OK;
03477       }
03478 
03479       // reset count and drop through to the code which samples the
03480       // clock and does the dynamic switch between the high
03481       // frequency and low frequency interruption of the parser.
03482 
03483       mDeflectedCount = 0;
03484     }
03485 
03486     mLastSampledUserEventTime = eventTime;
03487 
03488     PRUint32 currentTime = PR_IntervalToMicroseconds(PR_IntervalNow());
03489 
03490     // Get the last user event time and compare it with the current
03491     // time to determine if the lower value for content notification
03492     // and max token processing should be used. But only consider
03493     // using the lower value if the document has already been loading
03494     // for 2 seconds. 2 seconds was chosen because it is greater than
03495     // the default 3/4 of second that is used to determine when to
03496     // switch between the modes and it gives the document a little
03497     // time to create windows.  This is important because on some
03498     // systems (Windows, for example) when a window is created and the
03499     // mouse is over it, a mouse move event is sent, which will kick
03500     // us into interactive mode otherwise. It also supresses reaction
03501     // to pressing the ENTER key in the URL bar...
03502 
03503     PRUint32 delayBeforeLoweringThreshold =
03504       NS_STATIC_CAST(PRUint32, ((2 * mDynamicIntervalSwitchThreshold) +
03505                                 NS_DELAY_FOR_WINDOW_CREATION));
03506 
03507     if ((currentTime - mBeginLoadTime) > delayBeforeLoweringThreshold) {
03508       if ((currentTime - eventTime) <
03509           NS_STATIC_CAST(PRUint32, mDynamicIntervalSwitchThreshold)) {
03510     
03511         if (! (mFlags & NS_SINK_FLAG_DYNAMIC_LOWER_VALUE)) {
03512           // lower the dynamic values to favor application
03513           // responsiveness over page load time.
03514           mFlags |= NS_SINK_FLAG_DYNAMIC_LOWER_VALUE;
03515           // Set the performance hint to prevent event starvation when
03516           // dispatching PLEvents. This improves application responsiveness 
03517           // during page loads.
03518           PL_FavorPerformanceHint(PR_FALSE, 0);
03519         }
03520 
03521       } else {
03522       
03523         if (mFlags & NS_SINK_FLAG_DYNAMIC_LOWER_VALUE) {
03524           // raise the content notification and MaxTokenProcessing time
03525           // to favor overall page load speed over responsiveness.
03526           mFlags &= ~NS_SINK_FLAG_DYNAMIC_LOWER_VALUE;
03527           // Reset the hint that to favoring performance for PLEvent dispatch.
03528           PL_FavorPerformanceHint(PR_TRUE, 0);
03529         }
03530 
03531       }
03532     }
03533 
03534     if ((currentTime - mDelayTimerStart) >
03535         NS_STATIC_CAST(PRUint32, GetMaxTokenProcessingTime())) {
03536       return NS_ERROR_HTMLPARSER_INTERRUPTED;
03537     }
03538   }
03539 
03540   return NS_OK;
03541 }
03542 
03543 NS_IMETHODIMP
03544 HTMLContentSink::NotifyTagObservers(nsIParserNode* aNode)
03545 {
03546   // Bug 125317
03547   // Inform observers that we're handling a document.write().
03548   // This information is necessary for the charset observer, atleast,
03549   // to make a decision whether a new charset loading is required or not.
03550 
03551   if (!mObservers) {
03552     return NS_OK;
03553   }
03554 
03555   PRUint32 flag = 0;
03556 
03557   if (mHTMLDocument && mHTMLDocument->IsWriting()) {
03558     flag = nsIElementObserver::IS_DOCUMENT_WRITE;
03559   }
03560 
03561   return mObservers->Notify(aNode, mParser, mDocShell, flag);
03562 }
03563 
03564 void
03565 HTMLContentSink::StartLayout()
03566 {
03567   if (mLayoutStarted) {
03568     return;
03569   }
03570 
03571   mLayoutStarted = PR_TRUE;
03572 
03573   mLastNotificationTime = PR_Now();
03574 
03575   mHTMLDocument->SetIsFrameset(mFrameset != nsnull);
03576 
03577   nsContentSink::StartLayout(mFrameset != nsnull);
03578 }
03579 
03580 void
03581 HTMLContentSink::TryToScrollToRef()
03582 {
03583   if (mRef.IsEmpty()) {
03584     return;
03585   }
03586 
03587   if (mScrolledToRefAlready) {
03588     return;
03589   }
03590 
03591   if (ScrollToRef(PR_TRUE)) {
03592     mScrolledToRefAlready = PR_TRUE;
03593   }
03594 }
03595 
03596 void
03597 HTMLContentSink::AddBaseTagInfo(nsIContent* aContent)
03598 {
03599   if (!mBaseHREF.IsEmpty()) {
03600     aContent->SetAttr(kNameSpaceID_None, nsHTMLAtoms::_baseHref, mBaseHREF,
03601                       PR_FALSE);
03602   }
03603 
03604   if (!mBaseTarget.IsEmpty()) {
03605     aContent->SetAttr(kNameSpaceID_None, nsHTMLAtoms::_baseTarget,
03606                       mBaseTarget, PR_FALSE);
03607   }
03608 }
03609 
03610 nsresult
03611 HTMLContentSink::ProcessAREATag(const nsIParserNode& aNode)
03612 {
03613   if (!mCurrentMap) {
03614     return NS_OK;
03615   }
03616 
03617   nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
03618 
03619   nsRefPtr<nsGenericHTMLElement> area =
03620     CreateContentObject(aNode, nodeType, nsnull, nsnull);
03621   if (!area) {
03622     return NS_ERROR_OUT_OF_MEMORY;
03623   }
03624 
03625   // Make sure to add base tag info, if needed, before setting any other
03626   // attributes -- what URI attrs do will depend on the base URI.  Only do this
03627   // for elements that have useful URI attributes.
03628   // See bug 18478 and bug 30617 for why we need to do this.
03629   AddBaseTagInfo(area);
03630 
03631   // Set the content's attributes
03632   nsresult rv = AddAttributes(aNode, area);
03633   NS_ENSURE_SUCCESS(rv, rv);
03634 
03635   // Add AREA object to the current map
03636   mCurrentMap->AppendChildTo(area, PR_FALSE);
03637 
03638   return NS_OK;
03639 }
03640 
03641 void
03642 HTMLContentSink::ProcessBaseHref(const nsAString& aBaseHref)
03643 {
03644   //-- Make sure this page is allowed to load this URI
03645   nsresult rv;
03646   nsCOMPtr<nsIURI> baseHrefURI;
03647   rv = NS_NewURI(getter_AddRefs(baseHrefURI), aBaseHref, nsnull);
03648   if (NS_FAILED(rv)) return;
03649 
03650   // Setting "BASE URI" from the last BASE tag appearing in HEAD.
03651   if (!mBody) {
03652     // The document checks if it is legal to set this base
03653     rv = mDocument->SetBaseURI(baseHrefURI);
03654 
03655     if (NS_SUCCEEDED(rv)) {
03656       mDocumentBaseURI = mDocument->GetBaseURI();
03657     }
03658   } else {
03659     // NAV compatibility quirk
03660 
03661     nsIScriptSecurityManager *securityManager =
03662       nsContentUtils::GetSecurityManager();
03663 
03664     rv = securityManager->
03665       CheckLoadURIWithPrincipal(mDocument->GetPrincipal(), baseHrefURI,
03666                                 nsIScriptSecurityManager::STANDARD);
03667     if (NS_FAILED(rv)) {
03668       return;
03669     }
03670 
03671     mBaseHREF = aBaseHref;
03672   }
03673 }
03674 
03675 nsresult
03676 HTMLContentSink::OpenHeadContext()
03677 {
03678   if (mCurrentContext && mCurrentContext->IsCurrentContainer(eHTMLTag_head))
03679     return NS_OK;
03680 
03681   // Flush everything in the current context so that we don't have
03682   // to worry about insertions resulting in inconsistent frame creation.
03683   //
03684   // Try to do this only if needed (costly), i.e., only if we are sure
03685   // we are changing contexts from some other context to the head.
03686   //
03687   // PERF: This call causes approximately a 2% slowdown in page load time
03688   // according to jrgm's page load tests, but seems to be a necessary evil
03689   if (mCurrentContext && (mCurrentContext != mHeadContext)) {
03690     mCurrentContext->FlushTags(PR_TRUE);
03691   }
03692 
03693   if (!mHeadContext) {
03694     mHeadContext = new SinkContext(this);
03695     NS_ENSURE_TRUE(mHeadContext, NS_ERROR_OUT_OF_MEMORY);
03696 
03697     nsresult rv = mHeadContext->Begin(eHTMLTag_head, mHead, 0, -1);
03698     NS_ENSURE_SUCCESS(rv, rv);
03699   }
03700 
03701   mContextStack.AppendElement(mCurrentContext);
03702   mCurrentContext = mHeadContext;
03703 
03704   return NS_OK;
03705 }
03706 
03707 nsresult
03708 HTMLContentSink::CloseHeadContext()
03709 {
03710   if (mCurrentContext && !mCurrentContext->IsCurrentContainer(eHTMLTag_head))
03711     return NS_OK;
03712 
03713   PRInt32 n = mContextStack.Count() - 1;
03714   mCurrentContext = (SinkContext*) mContextStack.ElementAt(n);
03715   mContextStack.RemoveElementAt(n);
03716 
03717   return NS_OK;
03718 }
03719 
03720 void
03721 HTMLContentSink::ProcessBaseTarget(const nsAString& aBaseTarget)
03722 {
03723   if (!mBody) {
03724     // still in real HEAD
03725     mDocument->SetBaseTarget(aBaseTarget);
03726   } else {
03727     // NAV compatibility quirk
03728     mBaseTarget = aBaseTarget;
03729   }
03730 }
03731 
03732 nsresult
03733 HTMLContentSink::ProcessBASETag(const nsIParserNode& aNode)
03734 {
03735   nsresult result = NS_OK;
03736   nsGenericHTMLElement* parent = nsnull;
03737 
03738   if (mCurrentContext) {
03739     parent = mCurrentContext->mStack[mCurrentContext->mStackPos - 1].mContent;
03740   }
03741 
03742   if (parent) {
03743     // Create content object
03744     nsCOMPtr<nsIContent> element;
03745     nsCOMPtr<nsINodeInfo> nodeInfo;
03746     mNodeInfoManager->GetNodeInfo(nsHTMLAtoms::base, nsnull,
03747                                   kNameSpaceID_None,
03748                                   getter_AddRefs(nodeInfo));
03749 
03750     result = NS_NewHTMLElement(getter_AddRefs(element), nodeInfo);
03751     NS_ENSURE_SUCCESS(result, result);
03752 
03753     element->SetContentID(mDocument->GetAndIncrementContentID());
03754 
03755     // Add in the attributes and add the base content object to the
03756     // head container.
03757     result = AddAttributes(aNode, element);
03758     NS_ENSURE_SUCCESS(result, result);
03759 
03760     parent->AppendChildTo(element, PR_FALSE);
03761     if (!mInsideNoXXXTag) {
03762       nsAutoString value;
03763       if (element->GetAttr(kNameSpaceID_None, nsHTMLAtoms::href,
03764                            value) == NS_CONTENT_ATTR_HAS_VALUE) {
03765         ProcessBaseHref(value);
03766       }
03767 
03768       if (element->GetAttr(kNameSpaceID_None, nsHTMLAtoms::target,
03769                            value) == NS_CONTENT_ATTR_HAS_VALUE) {
03770         ProcessBaseTarget(value);
03771       }
03772     }
03773   }
03774 
03775   return result;
03776 }
03777 
03778 nsresult
03779 HTMLContentSink::ProcessLINKTag(const nsIParserNode& aNode)
03780 {
03781   nsresult  result = NS_OK;
03782   nsGenericHTMLElement* parent = nsnull;
03783 
03784   if (mCurrentContext) {
03785     parent = mCurrentContext->mStack[mCurrentContext->mStackPos - 1].mContent;
03786   }
03787 
03788   if (parent) {
03789     // Create content object
03790     nsCOMPtr<nsIContent> element;
03791     nsCOMPtr<nsINodeInfo> nodeInfo;
03792     mNodeInfoManager->GetNodeInfo(nsHTMLAtoms::link, nsnull, kNameSpaceID_None,
03793                                   getter_AddRefs(nodeInfo));
03794 
03795     result = NS_NewHTMLElement(getter_AddRefs(element), nodeInfo);
03796     NS_ENSURE_SUCCESS(result, result);
03797 
03798     element->SetContentID(mDocument->GetAndIncrementContentID());
03799 
03800     nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(element));
03801 
03802     if (ssle) {
03803       // XXX need prefs. check here.
03804       if (!mInsideNoXXXTag) {
03805         ssle->InitStyleLinkElement(mParser, PR_FALSE);
03806         ssle->SetEnableUpdates(PR_FALSE);
03807       } else {
03808         ssle->InitStyleLinkElement(nsnull, PR_TRUE);
03809       }
03810     }
03811 
03812     // Add in the attributes and add the style content object to the
03813     // head container.
03814     AddBaseTagInfo(element);
03815     result = AddAttributes(aNode, element);
03816     if (NS_FAILED(result)) {
03817       return result;
03818     }
03819     parent->AppendChildTo(element, PR_FALSE);
03820 
03821     if (ssle) {
03822       ssle->SetEnableUpdates(PR_TRUE);
03823       result = ssle->UpdateStyleSheet(nsnull, nsnull);
03824 
03825       // look for <link rel="next" href="url">
03826       nsAutoString relVal;
03827       element->GetAttr(kNameSpaceID_None, nsHTMLAtoms::rel, relVal);
03828       if (!relVal.IsEmpty()) {
03829         // XXX seems overkill to generate this string array
03830         nsStringArray linkTypes;
03831         nsStyleLinkElement::ParseLinkTypes(relVal, linkTypes);
03832         PRBool hasPrefetch = (linkTypes.IndexOf(NS_LITERAL_STRING("prefetch")) != -1);
03833         if (hasPrefetch || linkTypes.IndexOf(NS_LITERAL_STRING("next")) != -1) {
03834           nsAutoString hrefVal;
03835           element->GetAttr(kNameSpaceID_None, nsHTMLAtoms::href, hrefVal);
03836           if (!hrefVal.IsEmpty()) {
03837             PrefetchHref(hrefVal, hasPrefetch);
03838           }
03839         }
03840       }
03841     }
03842   }
03843 
03844   return result;
03845 }
03846 
03847 nsresult
03848 HTMLContentSink::ProcessMAPTag(nsIContent* aContent)
03849 {
03850   mCurrentMap = nsnull;
03851 
03852   nsCOMPtr<nsIDOMHTMLMapElement> domMap(do_QueryInterface(aContent));
03853   if (!domMap) {
03854     return NS_ERROR_UNEXPECTED;
03855   }
03856 
03857   // We used to strip whitespace from the NAME attribute here, to
03858   // match a 4.x quirk, but it proved too quirky for us, and IE never
03859   // did that.  See bug 79738 for details.
03860 
03861   // Don't need to add the map to the document here anymore.
03862   // The map adds itself
03863 
03864   mCurrentMap = aContent;
03865 
03866   return NS_OK;
03867 }
03868 
03869 nsresult
03870 HTMLContentSink::ProcessMETATag(const nsIParserNode& aNode)
03871 {
03872   nsGenericHTMLElement* parent = nsnull;
03873 
03874   if (mCurrentContext) {
03875     parent = mCurrentContext->mStack[mCurrentContext->mStackPos - 1].mContent;
03876   }
03877 
03878   if (!parent) {
03879     return NS_OK;
03880   }
03881 
03882   nsresult rv = NS_OK;
03883 
03884   // Create content object
03885   nsCOMPtr<nsINodeInfo> nodeInfo;
03886   rv = mNodeInfoManager->GetNodeInfo(nsHTMLAtoms::meta, nsnull,
03887                                      kNameSpaceID_None,
03888                                      getter_AddRefs(nodeInfo));
03889   NS_ENSURE_SUCCESS(rv, rv);
03890 
03891   nsRefPtr<nsGenericHTMLElement> it = NS_NewHTMLMetaElement(nodeInfo);
03892   if (!it) {
03893     return NS_ERROR_OUT_OF_MEMORY;
03894   }
03895 
03896   it->SetContentID(mDocument->GetAndIncrementContentID());
03897 
03898   // Add in the attributes and add the meta content object to the head
03899   // container.
03900   AddBaseTagInfo(it);
03901   rv = AddAttributes(aNode, it);
03902   if (NS_FAILED(rv)) {
03903     return rv;
03904   }
03905 
03906   parent->AppendChildTo(it, PR_FALSE);
03907 
03908   // XXX It's just not sufficient to check if the parent is head. Also
03909   // check for the preference.
03910   // Bug 40072: Don't evaluate METAs after FRAMESET.
03911   if (!mInsideNoXXXTag && !mFrameset) {
03912     rv = nsContentSink::ProcessMETATag(it);
03913   }
03914 
03915   return rv;
03916 }
03917 
03918 #ifdef DEBUG
03919 void
03920 HTMLContentSink::ForceReflow()
03921 {
03922   mCurrentContext->FlushTags(PR_TRUE);
03923 }
03924 #endif
03925 
03926 void
03927 HTMLContentSink::NotifyAppend(nsIContent* aContainer, PRUint32 aStartIndex)
03928 {
03929   if (aContainer->GetCurrentDoc() != mDocument) {
03930     // aContainer is not actually in our document anymore.... Just bail out of
03931     // here; notifying on our document for this append would be wrong.
03932     return;
03933   }
03934 
03935   mInNotification++;
03936 
03937   MOZ_TIMER_DEBUGLOG(("Save and stop: nsHTMLContentSink::NotifyAppend()\n"));
03938   MOZ_TIMER_SAVE(mWatch)
03939   MOZ_TIMER_STOP(mWatch);
03940 
03941   mDocument->ContentAppended(aContainer, aStartIndex);
03942   mLastNotificationTime = PR_Now();
03943 
03944   MOZ_TIMER_DEBUGLOG(("Restore: nsHTMLContentSink::NotifyAppend()\n"));
03945   MOZ_TIMER_RESTORE(mWatch);
03946 
03947   mInNotification--;
03948 }
03949 
03950 void
03951 HTMLContentSink::NotifyInsert(nsIContent* aContent,
03952                               nsIContent* aChildContent,
03953                               PRInt32 aIndexInContainer)
03954 {
03955   if (aContent->GetCurrentDoc() != mDocument) {
03956     // aContent is not actually in our document anymore.... Just bail out of
03957     // here; notifying on our document for this insert would be wrong.
03958     return;
03959   }
03960 
03961   mInNotification++;
03962 
03963   MOZ_TIMER_DEBUGLOG(("Save and stop: nsHTMLContentSink::NotifyInsert()\n"));
03964   MOZ_TIMER_SAVE(mWatch)
03965   MOZ_TIMER_STOP(mWatch);
03966 
03967   mDocument->ContentInserted(aContent, aChildContent, aIndexInContainer);
03968   mLastNotificationTime = PR_Now();
03969 
03970   MOZ_TIMER_DEBUGLOG(("Restore: nsHTMLContentSink::NotifyInsert()\n"));
03971   MOZ_TIMER_RESTORE(mWatch);
03972 
03973   mInNotification--;
03974 }
03975 
03976 PRBool
03977 HTMLContentSink::IsMonolithicContainer(nsHTMLTag aTag)
03978 {
03979   if (aTag == eHTMLTag_tr     ||
03980       aTag == eHTMLTag_select ||
03981       aTag == eHTMLTag_applet ||
03982       aTag == eHTMLTag_object) {
03983     return PR_TRUE;
03984   }
03985 
03986   return PR_FALSE;
03987 }
03988 
03989 PRBool
03990 HTMLContentSink::IsTimeToNotify()
03991 {
03992   if (!mNotifyOnTimer || !mLayoutStarted || !mBackoffCount ||
03993       mInMonolithicContainer) {
03994     return PR_FALSE;
03995   }
03996 
03997   PRTime now = PR_Now();
03998   PRInt64 interval, diff;
03999 
04000   LL_I2L(interval, GetNotificationInterval());
04001   LL_SUB(diff, now, mLastNotificationTime);
04002 
04003   if (LL_CMP(diff, >, interval)) {
04004     mBackoffCount--;
04005     return PR_TRUE;
04006   }
04007 
04008   return PR_FALSE;
04009 }
04010 
04011 void
04012 HTMLContentSink::UpdateAllContexts()
04013 {
04014   PRInt32 numContexts = mContextStack.Count();
04015   for (PRInt32 i = 0; i < numContexts; i++) {
04016     SinkContext* sc = (SinkContext*)mContextStack.ElementAt(i);
04017 
04018     sc->UpdateChildCounts();
04019   }
04020 
04021   mCurrentContext->UpdateChildCounts();
04022 }
04023 
04024 void
04025 HTMLContentSink::BeginUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
04026 {
04027   // If we're in a script and we didn't do the notification,
04028   // something else in the script processing caused the
04029   // notification to occur. Since this could result in frame
04030   // creation, make sure we've flushed everything before we
04031   // continue.
04032   // Also increment mInNotification to make sure we don't flush again
04033   // until the end of this update, even if nested updates or
04034   // FlushPendingNotifications calls happen during it.
04035   if (!mInNotification++ && mCurrentContext) {
04036     mCurrentContext->FlushTags(PR_TRUE);
04037   }
04038 }
04039 
04040 void
04041 HTMLContentSink::EndUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
04042 {
04043   // If we're in a script and we didn't do the notification,
04044   // something else in the script processing caused the
04045   // notification to occur. Update our notion of how much
04046   // has been flushed to include any new content if ending
04047   // this update leaves us not inside a notification.
04048   if (!--mInNotification) {
04049     UpdateAllContexts();
04050   }
04051 }
04052 
04053 void
04054 HTMLContentSink::PreEvaluateScript()
04055 {
04056   // Eagerly append all pending elements (including the current body child)
04057   // to the body (so that they can be seen by scripts) and force reflow.
04058   SINK_TRACE(SINK_TRACE_CALLS,
04059              ("HTMLContentSink::PreEvaluateScript: flushing tags before "
04060               "evaluating script"));
04061 
04062   mCurrentContext->FlushTags(PR_FALSE);
04063 }
04064 
04065 void
04066 HTMLContentSink::PostEvaluateScript()
04067 {
04068 }
04069 
04070 nsresult
04071 HTMLContentSink::ProcessSCRIPTTag(const nsIParserNode& aNode)
04072 {
04073   nsresult rv = NS_OK;
04074 
04075   // Create content object
04076   NS_ASSERTION(mCurrentContext->mStackPos > 0, "leaf w/o container");
04077   if (mCurrentContext->mStackPos <= 0) {
04078     return NS_ERROR_FAILURE;
04079   }
04080 
04081   // Inserting the element into the document may execute a script.
04082   // This can potentially make the parent go away. So, hold
04083   // on to it till we are done.
04084   nsRefPtr<nsGenericHTMLElement> parent =
04085     mCurrentContext->mStack[mCurrentContext->mStackPos - 1].mContent;
04086   nsCOMPtr<nsIContent> element;
04087   nsCOMPtr<nsINodeInfo> nodeInfo;
04088   mNodeInfoManager->GetNodeInfo(nsHTMLAtoms::script, nsnull, kNameSpaceID_None,
04089                                 getter_AddRefs(nodeInfo));
04090 
04091   rv = NS_NewHTMLElement(getter_AddRefs(element), nodeInfo);
04092   if (NS_FAILED(rv)) {
04093     return rv;
04094   }
04095 
04096   element->SetContentID(mDocument->GetAndIncrementContentID());
04097 
04098   // Add in the attributes and add the script content object to the
04099   // head container.
04100   AddBaseTagInfo(element);
04101   rv = AddAttributes(aNode, element);
04102   if (NS_FAILED(rv)) {
04103     return rv;
04104   }
04105 
04106   nsCOMPtr<nsIDTD> dtd;
04107   mParser->GetDTD(getter_AddRefs(dtd));
04108   NS_ENSURE_TRUE(dtd, NS_ERROR_FAILURE);
04109 
04110   nsCOMPtr<nsIScriptElement> sele(do_QueryInterface(element));
04111   nsAutoString script;
04112   PRInt32 lineNo = 0;
04113 
04114   dtd->CollectSkippedContent(eHTMLTag_script, script, lineNo);
04115 
04116   if (sele) {
04117     sele->SetScriptLineNumber((PRUint32)lineNo);
04118   }
04119 
04120   // Create a text node holding the content. First, get the text
04121   // content of the script tag
04122 
04123   if (!script.IsEmpty()) {
04124     nsCOMPtr<nsITextContent> text;
04125     rv = NS_NewTextNode(getter_AddRefs(text), mNodeInfoManager);
04126     NS_ENSURE_SUCCESS(rv, rv);
04127 
04128     text->SetText(script, PR_TRUE);
04129 
04130     element->AppendChildTo(text, PR_FALSE);
04131   }
04132 
04133   nsCOMPtr<nsIScriptLoader> loader;
04134   if (mFrameset) {
04135     // Fix bug 82498
04136     // We don't want to evaluate scripts in a frameset document.
04137     if (mDocument) {
04138       loader = mDocument->GetScriptLoader();
04139       if (loader) {
04140         loader->SetEnabled(PR_FALSE);
04141       }
04142     }
04143   } else if (parent->GetCurrentDoc() == mDocument) {
04144     // We test the current doc of |parent| because if it doesn't have one we
04145     // won't actually try to evaluate the script, so we shouldn't be blocking
04146     // or appending to mScriptElements or anything.
04147     
04148     // Don't include script loading and evaluation in the stopwatch
04149     // that is measuring content creation time
04150     MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::ProcessSCRIPTTag()\n"));
04151     MOZ_TIMER_STOP(mWatch);
04152 
04153     // Assume that we're going to block the parser with a script load.
04154     // If it's an inline script, we'll be told otherwise in the call
04155     // to our ScriptAvailable method.
04156     mNeedToBlockParser = PR_TRUE;
04157 
04158     mScriptElements.AppendObject(sele);
04159   }
04160 
04161   // Now flush out tags so that the script will actually be bound to a
04162   // document and will evaluate as soon as it's appended.
04163   SINK_TRACE(SINK_TRACE_CALLS,
04164              ("HTMLContentSink::ProcessSCRIPTTag: flushing tags before "
04165               "appending script"));
04166   mCurrentContext->FlushTags(PR_FALSE);
04167   
04168   // Insert the child into the content tree. This will evaluate the
04169   // script as well.
04170   if (mCurrentContext->mStack[mCurrentContext->mStackPos - 1].mInsertionPoint != -1) {
04171     parent->InsertChildAt(element,
04172                           mCurrentContext->mStack[mCurrentContext->mStackPos - 1].mInsertionPoint++,
04173                           PR_FALSE);
04174   } else {
04175     parent->AppendChildTo(element, PR_FALSE);
04176   }
04177 
04178   // To prevent script evaluation in a frameset document, we suspended
04179   // the script loader. Now that the script content has been handled,
04180   // let's resume the script loader.
04181   if (loader) {
04182     loader->SetEnabled(PR_TRUE);
04183   }
04184 
04185   // If the act of insertion evaluated the script, we're fine.
04186   // Else, block the parser till the script has loaded.
04187   if (mNeedToBlockParser || (mParser && !mParser->IsParserEnabled())) {
04188     return NS_ERROR_HTMLPARSER_BLOCK;
04189   }
04190 
04191   return NS_OK;
04192 }
04193 
04194 // 3 ways to load a style sheet: inline, style src=, link tag
04195 // XXX What does nav do if we have SRC= and some style data inline?
04196 
04197 nsresult
04198 HTMLContentSink::ProcessSTYLETag(const nsIParserNode& aNode)
04199 {
04200   nsresult rv = NS_OK;
04201   nsGenericHTMLElement* parent = nsnull;
04202 
04203   if (mCurrentContext) {
04204     parent = mCurrentContext->mStack[mCurrentContext->mStackPos - 1].mContent;
04205   }
04206 
04207   if (!parent) {
04208     return NS_OK;
04209   }
04210 
04211   // Create content object
04212   nsCOMPtr<nsINodeInfo> nodeInfo;
04213   mNodeInfoManager->GetNodeInfo(nsHTMLAtoms::style, nsnull, kNameSpaceID_None,
04214                                 getter_AddRefs(nodeInfo));
04215 
04216   nsCOMPtr<nsIContent> element;
04217   rv = NS_NewHTMLElement(getter_AddRefs(element), nodeInfo);
04218   NS_ENSURE_SUCCESS(rv, rv);
04219 
04220   element->SetContentID(mDocument->GetAndIncrementContentID());
04221 
04222   nsCOMPtr<nsIStyleSheetLinkingElement> ssle = do_QueryInterface(element);
04223 
04224   NS_ASSERTION(ssle,
04225                "html:style doesn't implement nsIStyleSheetLinkingElement");
04226 
04227   if (ssle) {
04228     // XXX need prefs. check here.
04229     if (!mInsideNoXXXTag) {
04230       ssle->InitStyleLinkElement(mParser, PR_FALSE);
04231       ssle->SetEnableUpdates(PR_FALSE);
04232     } else {
04233       ssle->InitStyleLinkElement(nsnull, PR_TRUE);
04234     }
04235   }
04236 
04237   // Add in the attributes and add the style content object to the
04238   // head container.
04239   AddBaseTagInfo(element);
04240   rv = AddAttributes(aNode, element);
04241   if (NS_FAILED(rv)) {
04242     return rv;
04243   }
04244 
04245   // The skipped content contains the inline style data
04246   nsCOMPtr<nsIDTD> dtd;
04247   mParser->GetDTD(getter_AddRefs(dtd));
04248   NS_ENSURE_TRUE(dtd, NS_ERROR_FAILURE);
04249 
04250   nsAutoString content;
04251   PRInt32 lineNo = 0;
04252 
04253   dtd->CollectSkippedContent(eHTMLTag_style, content, lineNo);
04254 
04255   if (ssle) {
04256     ssle->SetLineNumber(lineNo);
04257   }
04258 
04259   if (!content.IsEmpty()) {
04260     // Create a text node holding the content
04261     nsCOMPtr<nsITextContent> text;
04262     rv = NS_NewTextNode(getter_AddRefs(text), mNodeInfoManager);
04263     NS_ENSURE_SUCCESS(rv, rv);
04264 
04265     text->SetText(content, PR_TRUE);
04266 
04267     element->AppendChildTo(text, PR_FALSE);
04268   }
04269 
04270   parent->AppendChildTo(element, PR_FALSE);
04271 
04272   if (ssle) {
04273     ssle->SetEnableUpdates(PR_TRUE);
04274     rv = ssle->UpdateStyleSheet(nsnull, nsnull);
04275   }
04276 
04277   return rv;
04278 }
04279 
04280 void
04281 HTMLContentSink::FlushPendingNotifications(mozFlushType aType)
04282 {
04283   // Only flush tags if we're not doing the notification ourselves
04284   // (since we aren't reentrant)
04285   if (mCurrentContext && !mInNotification) {
04286     PRBool notify = ((aType & Flush_SinkNotifications) != 0);
04287     mCurrentContext->FlushTags(notify);
04288     if (aType & Flush_OnlyReflow) {
04289       // Make sure that layout has started so that the reflow flush
04290       // will actually happen.
04291       StartLayout();
04292     }
04293   }
04294 }
04295 
04296 NS_IMETHODIMP
04297 HTMLContentSink::SetDocumentCharset(nsACString& aCharset)
04298 {
04299   if (mDocShell) {
04300     // the following logic to get muCV is copied from
04301     // nsHTMLDocument::StartDocumentLoad
04302     // We need to call muCV->SetPrevDocCharacterSet here in case
04303     // the charset is detected by parser DetectMetaTag
04304     nsCOMPtr<nsIMarkupDocumentViewer> muCV;
04305     nsCOMPtr<nsIContentViewer> cv;
04306     mDocShell->GetContentViewer(getter_AddRefs(cv));
04307     if (cv) {
04308        muCV = do_QueryInterface(cv);
04309     } else {
04310       // in this block of code, if we get an error result, we return
04311       // it but if we get a null pointer, that's perfectly legal for
04312       // parent and parentContentViewer
04313 
04314       nsCOMPtr<nsIDocShellTreeItem> docShellAsItem =
04315         do_QueryInterface(mDocShell);
04316       NS_ENSURE_TRUE(docShellAsItem, NS_ERROR_FAILURE);
04317 
04318       nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
04319       docShellAsItem->GetSameTypeParent(getter_AddRefs(parentAsItem));
04320 
04321       nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
04322       if (parent) {
04323         nsCOMPtr<nsIContentViewer> parentContentViewer;
04324         nsresult rv =
04325           parent->GetContentViewer(getter_AddRefs(parentContentViewer));
04326         if (NS_SUCCEEDED(rv) && parentContentViewer) {
04327           muCV = do_QueryInterface(parentContentViewer);
04328         }
04329       }
04330     }
04331 
04332     if (muCV) {
04333       muCV->SetPrevDocCharacterSet(aCharset);
04334     }
04335   }
04336 
04337   if (mDocument) {
04338     mDocument->SetDocumentCharacterSet(aCharset);
04339   }
04340 
04341   return NS_OK;
04342 }
04343 
04344 nsISupports *
04345 HTMLContentSink::GetTarget()
04346 {
04347   return mDocument;
04348 }
04349 
04350 #ifdef DEBUG
04351 
04359 NS_IMETHODIMP
04360 HTMLContentSink::DumpContentModel()
04361 {
04362   FILE* out = ::fopen("rtest_html.txt", "a");
04363   if (out) {
04364     if (mDocument) {
04365       nsIContent* root = mDocument->GetRootContent();
04366       if (root) {
04367         if (mDocumentURI) {
04368           nsCAutoString buf;
04369           mDocumentURI->GetSpec(buf);
04370           fputs(buf.get(), out);
04371         }
04372 
04373         fputs(";", out);
04374         root->DumpContent(out, 0, PR_FALSE);
04375         fputs(";\n", out);
04376       }
04377     }
04378 
04379     fclose(out);
04380   }
04381 
04382   return NS_OK;
04383 }
04384 #endif
04385 
04386 // If the content sink can interrupt the parser (@see mCanInteruptParsing)
04387 // then it needs to schedule a dummy parser request to delay the document
04388 // from firing onload handlers and other document done actions until all of the
04389 // parsing has completed.
04390 
04391 nsresult
04392 HTMLContentSink::AddDummyParserRequest(void)
04393 {
04394   nsresult rv = NS_OK;
04395 
04396   NS_ASSERTION(!mDummyParserRequest, "Already have a dummy parser request");
04397 
04398   rv = DummyParserRequest::Create(getter_AddRefs(mDummyParserRequest), this);
04399   if (NS_FAILED(rv)) {
04400     return rv;
04401   }
04402 
04403   nsCOMPtr<nsILoadGroup> loadGroup;
04404   if (mDocument) {
04405     loadGroup = mDocument->GetDocumentLoadGroup();
04406   }
04407 
04408   if (loadGroup) {
04409     rv = mDummyParserRequest->SetLoadGroup(loadGroup);
04410     if (NS_FAILED(rv)) {
04411       return rv;
04412     }
04413 
04414     rv = loadGroup->AddRequest(mDummyParserRequest, nsnull);
04415   }
04416 
04417   return rv;
04418 }
04419 
04420 nsresult
04421 HTMLContentSink::RemoveDummyParserRequest(void)
04422 {
04423   nsresult rv = NS_OK;
04424 
04425   nsCOMPtr<nsILoadGroup> loadGroup;
04426   if (mDocument) {
04427     loadGroup = mDocument->GetDocumentLoadGroup();
04428   }
04429 
04430   if (loadGroup && mDummyParserRequest) {
04431     rv = loadGroup->RemoveRequest(mDummyParserRequest, nsnull, NS_OK);
04432     if (NS_FAILED(rv)) {
04433       return rv;
04434     }
04435 
04436     mDummyParserRequest = nsnull;
04437   }
04438 
04439   return rv;
04440 }
04441