Bug 570258: Some more atom usage cleanup. r=jst
[mozilla-central.git] / content / html / document / src / nsHTMLContentSink.cpp
blob0ee07a85b924c5e2a22c05278b40025dc1f6e221
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=2 et tw=78: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Pierre Phaneuf <pp@ludusdesign.com>
25 * Peter Annema <disttsc@bart.nl>
26 * Daniel Glazman <glazman@netscape.com>
27 * Henri Sivonen <hsivonen@iki.fi>
29 * Alternatively, the contents of this file may be used under the terms of
30 * either of the GNU General Public License Version 2 or later (the "GPL"),
31 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 * in which case the provisions of the GPL or the LGPL are applicable instead
33 * of those above. If you wish to allow use of your version of this file only
34 * under the terms of either the GPL or the LGPL, and not to allow others to
35 * use your version of this file under the terms of the MPL, indicate your
36 * decision by deleting the provisions above and replace them with the notice
37 * and other provisions required by the GPL or the LGPL. If you do not delete
38 * the provisions above, a recipient may use your version of this file under
39 * the terms of any one of the MPL, the GPL or the LGPL.
41 * ***** END LICENSE BLOCK ***** */
43 #include "nsContentSink.h"
44 #include "nsCOMPtr.h"
45 #include "nsReadableUtils.h"
46 #include "nsUnicharUtils.h"
47 #include "nsIHTMLContentSink.h"
48 #include "nsIInterfaceRequestor.h"
49 #include "nsIInterfaceRequestorUtils.h"
50 #include "nsIParser.h"
51 #include "nsParserUtils.h"
52 #include "nsScriptLoader.h"
53 #include "nsIURI.h"
54 #include "nsNetUtil.h"
55 #include "nsIContentViewer.h"
56 #include "nsIMarkupDocumentViewer.h"
57 #include "nsINodeInfo.h"
58 #include "nsHTMLTokens.h"
59 #include "nsIAppShell.h"
60 #include "nsCRT.h"
61 #include "prtime.h"
62 #include "prlog.h"
63 #include "nsInt64.h"
64 #include "nsNodeUtils.h"
65 #include "nsIContent.h"
66 #include "mozilla/dom/Element.h"
68 #include "nsGenericHTMLElement.h"
70 #include "nsIDOMText.h"
71 #include "nsIDOMComment.h"
72 #include "nsIDOMDocument.h"
73 #include "nsIDOMNSDocument.h"
74 #include "nsIDOMDOMImplementation.h"
75 #include "nsIDOMDocumentType.h"
76 #include "nsIDOMHTMLScriptElement.h"
77 #include "nsIScriptElement.h"
79 #include "nsIDOMHTMLFormElement.h"
80 #include "nsIDOMHTMLTextAreaElement.h"
81 #include "nsIFormControl.h"
82 #include "nsIForm.h"
84 #include "nsIComponentManager.h"
85 #include "nsIServiceManager.h"
87 #include "nsGkAtoms.h"
88 #include "nsContentUtils.h"
89 #include "nsIChannel.h"
90 #include "nsIHttpChannel.h"
91 #include "nsIDocShell.h"
92 #include "nsIDocument.h"
93 #include "nsStubDocumentObserver.h"
94 #include "nsIHTMLDocument.h"
95 #include "nsINameSpaceManager.h"
96 #include "nsIDOMHTMLMapElement.h"
97 #include "nsICookieService.h"
98 #include "nsTArray.h"
99 #include "nsIScriptSecurityManager.h"
100 #include "nsIPrincipal.h"
101 #include "nsTextFragment.h"
102 #include "nsIScriptGlobalObject.h"
103 #include "nsIScriptGlobalObjectOwner.h"
105 #include "nsIParserService.h"
106 #include "nsISelectElement.h"
108 #include "nsIStyleSheetLinkingElement.h"
109 #include "nsITimer.h"
110 #include "nsDOMError.h"
111 #include "nsContentPolicyUtils.h"
112 #include "nsIScriptContext.h"
113 #include "nsStyleLinkElement.h"
115 #include "nsReadableUtils.h"
116 #include "nsWeakReference.h" // nsHTMLElementFactory supports weak references
117 #include "nsIPrompt.h"
118 #include "nsLayoutCID.h"
119 #include "nsIDocShellTreeItem.h"
121 #include "nsEscape.h"
122 #include "nsIElementObserver.h"
123 #include "nsNodeInfoManager.h"
124 #include "nsContentCreatorFunctions.h"
125 #include "mozAutoDocUpdate.h"
127 using namespace mozilla::dom;
129 #ifdef NS_DEBUG
130 static PRLogModuleInfo* gSinkLogModuleInfo;
132 #define SINK_TRACE_NODE(_bit, _msg, _tag, _sp, _obj) \
133 _obj->SinkTraceNode(_bit, _msg, _tag, _sp, this)
135 #else
136 #define SINK_TRACE_NODE(_bit, _msg, _tag, _sp, _obj)
137 #endif
139 //----------------------------------------------------------------------
141 typedef nsGenericHTMLElement* (*contentCreatorCallback)(nsINodeInfo*, PRBool aFromParser);
143 nsGenericHTMLElement*
144 NS_NewHTMLNOTUSEDElement(nsINodeInfo *aNodeInfo, PRBool aFromParser)
146 NS_NOTREACHED("The element ctor should never be called");
147 return nsnull;
150 #define HTML_TAG(_tag, _classname) NS_NewHTML##_classname##Element,
151 #define HTML_OTHER(_tag) NS_NewHTMLNOTUSEDElement,
152 static const contentCreatorCallback sContentCreatorCallbacks[] = {
153 NS_NewHTMLUnknownElement,
154 #include "nsHTMLTagList.h"
155 #undef HTML_TAG
156 #undef HTML_OTHER
157 NS_NewHTMLUnknownElement
160 class SinkContext;
161 class HTMLContentSink;
163 static void MaybeSetForm(nsGenericHTMLElement*, nsHTMLTag, HTMLContentSink*);
165 class HTMLContentSink : public nsContentSink,
166 #ifdef DEBUG
167 public nsIDebugDumpContent,
168 #endif
169 public nsIHTMLContentSink
171 public:
172 friend class SinkContext;
173 friend void MaybeSetForm(nsGenericHTMLElement*, nsHTMLTag, HTMLContentSink*);
175 HTMLContentSink();
176 virtual ~HTMLContentSink();
178 NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
180 nsresult Init(nsIDocument* aDoc, nsIURI* aURI, nsISupports* aContainer,
181 nsIChannel* aChannel);
183 // nsISupports
184 NS_DECL_ISUPPORTS_INHERITED
185 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLContentSink, nsContentSink)
187 // nsIContentSink
188 NS_IMETHOD WillParse(void);
189 NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode);
190 NS_IMETHOD DidBuildModel(PRBool aTerminated);
191 NS_IMETHOD WillInterrupt(void);
192 NS_IMETHOD WillResume(void);
193 NS_IMETHOD SetParser(nsIParser* aParser);
194 virtual void FlushPendingNotifications(mozFlushType aType);
195 NS_IMETHOD SetDocumentCharset(nsACString& aCharset);
196 virtual nsISupports *GetTarget();
197 virtual PRBool IsScriptExecuting();
199 // nsIHTMLContentSink
200 NS_IMETHOD OpenContainer(const nsIParserNode& aNode);
201 NS_IMETHOD CloseContainer(const nsHTMLTag aTag);
202 NS_IMETHOD CloseMalformedContainer(const nsHTMLTag aTag);
203 NS_IMETHOD AddLeaf(const nsIParserNode& aNode);
204 NS_IMETHOD AddComment(const nsIParserNode& aNode);
205 NS_IMETHOD AddProcessingInstruction(const nsIParserNode& aNode);
206 NS_IMETHOD AddDocTypeDecl(const nsIParserNode& aNode);
207 NS_IMETHOD DidProcessTokens(void);
208 NS_IMETHOD WillProcessAToken(void);
209 NS_IMETHOD DidProcessAToken(void);
210 NS_IMETHOD NotifyTagObservers(nsIParserNode* aNode);
211 NS_IMETHOD BeginContext(PRInt32 aID);
212 NS_IMETHOD EndContext(PRInt32 aID);
213 NS_IMETHOD OpenHead();
214 NS_IMETHOD IsEnabled(PRInt32 aTag, PRBool* aReturn);
215 NS_IMETHOD_(PRBool) IsFormOnStack();
217 #ifdef DEBUG
218 // nsIDebugDumpContent
219 NS_IMETHOD DumpContentModel();
220 #endif
222 protected:
223 // If aCheckIfPresent is true, will only set an attribute in cases
224 // when it's not already set.
225 nsresult AddAttributes(const nsIParserNode& aNode, nsIContent* aContent,
226 PRBool aNotify = PR_FALSE,
227 PRBool aCheckIfPresent = PR_FALSE);
228 already_AddRefed<nsGenericHTMLElement>
229 CreateContentObject(const nsIParserNode& aNode, nsHTMLTag aNodeType);
231 #ifdef NS_DEBUG
232 void SinkTraceNode(PRUint32 aBit,
233 const char* aMsg,
234 const nsHTMLTag aTag,
235 PRInt32 aStackPos,
236 void* aThis);
237 #endif
239 nsCOMPtr<nsIHTMLDocument> mHTMLDocument;
241 // The maximum length of a text run
242 PRInt32 mMaxTextRun;
244 nsRefPtr<nsGenericHTMLElement> mRoot;
245 nsRefPtr<nsGenericHTMLElement> mBody;
246 nsRefPtr<nsGenericHTMLElement> mFrameset;
247 nsRefPtr<nsGenericHTMLElement> mHead;
249 nsRefPtr<nsGenericHTMLElement> mCurrentForm;
251 nsAutoTArray<SinkContext*, 8> mContextStack;
252 SinkContext* mCurrentContext;
253 SinkContext* mHeadContext;
254 PRInt32 mNumOpenIFRAMES;
256 // depth of containment within <noembed>, <noframes> etc
257 PRInt32 mInsideNoXXXTag;
259 // Boolean indicating whether we've seen a <head> tag that might have had
260 // attributes once already.
261 PRPackedBool mHaveSeenHead;
263 // Boolean indicating whether we've notified insertion of our root content
264 // yet. We want to make sure to only do this once.
265 PRPackedBool mNotifiedRootInsertion;
267 PRUint8 mScriptEnabled : 1;
268 PRUint8 mFramesEnabled : 1;
269 PRUint8 mFormOnStack : 1;
270 PRUint8 unused : 5; // bits available if someone needs one
272 nsCOMPtr<nsIObserverEntry> mObservers;
274 nsINodeInfo* mNodeInfoCache[NS_HTML_TAG_MAX + 1];
276 nsresult FlushTags();
278 void StartLayout(PRBool aIgnorePendingSheets);
280 // Routines for tags that require special handling
281 nsresult CloseHTML();
282 nsresult OpenFrameset(const nsIParserNode& aNode);
283 nsresult CloseFrameset();
284 nsresult OpenBody(const nsIParserNode& aNode);
285 nsresult CloseBody();
286 nsresult OpenForm(const nsIParserNode& aNode);
287 nsresult CloseForm();
288 nsresult ProcessLINKTag(const nsIParserNode& aNode);
290 // Routines for tags that require special handling when we reach their end
291 // tag.
292 nsresult ProcessSCRIPTEndTag(nsGenericHTMLElement* content,
293 PRBool aMalformed);
294 nsresult ProcessSTYLEEndTag(nsGenericHTMLElement* content);
296 nsresult OpenHeadContext();
297 void CloseHeadContext();
299 // nsContentSink overrides
300 virtual void PreEvaluateScript();
301 virtual void PostEvaluateScript(nsIScriptElement *aElement);
303 void UpdateChildCounts();
305 void NotifyInsert(nsIContent* aContent,
306 nsIContent* aChildContent,
307 PRInt32 aIndexInContainer);
308 void NotifyRootInsertion();
310 PRBool IsMonolithicContainer(nsHTMLTag aTag);
312 #ifdef NS_DEBUG
313 void ForceReflow();
314 #endif
317 class SinkContext
319 public:
320 SinkContext(HTMLContentSink* aSink);
321 ~SinkContext();
323 nsresult Begin(nsHTMLTag aNodeType, nsGenericHTMLElement* aRoot,
324 PRUint32 aNumFlushed, PRInt32 aInsertionPoint);
325 nsresult OpenContainer(const nsIParserNode& aNode);
326 nsresult CloseContainer(const nsHTMLTag aTag, PRBool aMalformed);
327 nsresult AddLeaf(const nsIParserNode& aNode);
328 nsresult AddLeaf(nsIContent* aContent);
329 nsresult AddComment(const nsIParserNode& aNode);
330 nsresult End();
332 nsresult GrowStack();
333 nsresult AddText(const nsAString& aText);
334 nsresult FlushText(PRBool* aDidFlush = nsnull,
335 PRBool aReleaseLast = PR_FALSE);
336 nsresult FlushTextAndRelease(PRBool* aDidFlush = nsnull)
338 return FlushText(aDidFlush, PR_TRUE);
341 nsresult FlushTags();
343 PRBool IsCurrentContainer(nsHTMLTag mType);
344 PRBool IsAncestorContainer(nsHTMLTag mType);
346 void DidAddContent(nsIContent* aContent);
347 void UpdateChildCounts();
349 private:
350 // Function to check whether we've notified for the current content.
351 // What this actually does is check whether we've notified for all
352 // of the parent's kids.
353 PRBool HaveNotifiedForCurrentContent() const;
355 public:
356 HTMLContentSink* mSink;
357 PRInt32 mNotifyLevel;
358 nsCOMPtr<nsIContent> mLastTextNode;
359 PRInt32 mLastTextNodeSize;
361 struct Node {
362 nsHTMLTag mType;
363 nsGenericHTMLElement* mContent;
364 PRUint32 mNumFlushed;
365 PRInt32 mInsertionPoint;
367 nsIContent *Add(nsIContent *child);
370 Node* mStack;
371 PRInt32 mStackSize;
372 PRInt32 mStackPos;
374 PRUnichar* mText;
375 PRInt32 mTextLength;
376 PRInt32 mTextSize;
378 private:
379 PRBool mLastTextCharWasCR;
382 //----------------------------------------------------------------------
384 #ifdef NS_DEBUG
385 void
386 HTMLContentSink::SinkTraceNode(PRUint32 aBit,
387 const char* aMsg,
388 const nsHTMLTag aTag,
389 PRInt32 aStackPos,
390 void* aThis)
392 if (SINK_LOG_TEST(gSinkLogModuleInfo, aBit)) {
393 nsIParserService *parserService = nsContentUtils::GetParserService();
394 if (!parserService)
395 return;
397 NS_ConvertUTF16toUTF8 tag(parserService->HTMLIdToStringTag(aTag));
398 PR_LogPrint("%s: this=%p node='%s' stackPos=%d",
399 aMsg, aThis, tag.get(), aStackPos);
402 #endif
404 nsresult
405 HTMLContentSink::AddAttributes(const nsIParserNode& aNode,
406 nsIContent* aContent, PRBool aNotify,
407 PRBool aCheckIfPresent)
409 // Add tag attributes to the content attributes
411 PRInt32 ac = aNode.GetAttributeCount();
413 if (ac == 0) {
414 // No attributes, nothing to do. Do an early return to avoid
415 // constructing the nsAutoString object for nothing.
417 return NS_OK;
420 nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
422 // The attributes are on the parser node in the order they came in in the
423 // source. What we want to happen if a single attribute is set multiple
424 // times on an element is that the first time should "win". That is, <input
425 // value="foo" value="bar"> should show "foo". So we loop over the
426 // attributes backwards; this ensures that the first attribute in the set
427 // wins. This does mean that we do some extra work in the case when the same
428 // attribute is set multiple times, but we save a HasAttr call in the much
429 // more common case of reasonable HTML. Note that if aCheckIfPresent is set
430 // then we actually want to loop _forwards_ to preserve the "first attribute
431 // wins" behavior. That does mean that when aCheckIfPresent is set the order
432 // of attributes will get "reversed" from the point of view of the
433 // serializer. But aCheckIfPresent is only true for malformed documents with
434 // multiple <html>, <head>, or <body> tags, so we're doing fixup anyway at
435 // that point.
437 PRInt32 i, limit, step;
438 if (aCheckIfPresent) {
439 i = 0;
440 limit = ac;
441 step = 1;
442 } else {
443 i = ac - 1;
444 limit = -1;
445 step = -1;
448 nsAutoString key;
449 for (; i != limit; i += step) {
450 // Get lower-cased key
451 nsContentUtils::ASCIIToLower(aNode.GetKeyAt(i), key);
453 nsCOMPtr<nsIAtom> keyAtom = do_GetAtom(key);
455 if (aCheckIfPresent && aContent->HasAttr(kNameSpaceID_None, keyAtom)) {
456 continue;
459 // Get value and remove mandatory quotes
460 static const char* kWhitespace = "\n\r\t\b";
462 // Bug 114997: Don't trim whitespace on <input value="...">:
463 // Using ?: outside the function call would be more efficient, but
464 // we don't trust ?: with references.
465 const nsAString& v =
466 nsContentUtils::TrimCharsInSet(
467 (nodeType == eHTMLTag_input &&
468 keyAtom == nsGkAtoms::value) ?
469 "" : kWhitespace, aNode.GetValueAt(i));
471 if (nodeType == eHTMLTag_a && keyAtom == nsGkAtoms::name) {
472 NS_ConvertUTF16toUTF8 cname(v);
473 NS_ConvertUTF8toUTF16 uv(nsUnescape(cname.BeginWriting()));
475 // Add attribute to content
476 aContent->SetAttr(kNameSpaceID_None, keyAtom, uv, aNotify);
477 } else {
478 // Add attribute to content
479 aContent->SetAttr(kNameSpaceID_None, keyAtom, v, aNotify);
483 return NS_OK;
486 static void
487 MaybeSetForm(nsGenericHTMLElement* aContent, nsHTMLTag aNodeType,
488 HTMLContentSink* aSink)
490 nsGenericHTMLElement* form = aSink->mCurrentForm;
492 if (!form || aSink->mInsideNoXXXTag) {
493 return;
496 switch (aNodeType) {
497 case eHTMLTag_button:
498 case eHTMLTag_fieldset:
499 case eHTMLTag_label:
500 case eHTMLTag_legend:
501 case eHTMLTag_object:
502 case eHTMLTag_input:
503 case eHTMLTag_select:
504 case eHTMLTag_textarea:
505 break;
506 default:
507 return;
510 nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(aContent));
511 NS_ASSERTION(formControl,
512 "nsGenericHTMLElement didn't implement nsIFormControl");
513 nsCOMPtr<nsIDOMHTMLFormElement> formElement(do_QueryInterface(form));
514 NS_ASSERTION(formElement,
515 "nsGenericHTMLElement didn't implement nsIDOMHTMLFormElement");
517 formControl->SetForm(formElement);
521 * Factory subroutine to create all of the html content objects.
523 already_AddRefed<nsGenericHTMLElement>
524 HTMLContentSink::CreateContentObject(const nsIParserNode& aNode,
525 nsHTMLTag aNodeType)
527 // Find/create atom for the tag name
529 nsCOMPtr<nsINodeInfo> nodeInfo;
531 if (aNodeType == eHTMLTag_userdefined) {
532 nsAutoString lower;
533 nsContentUtils::ASCIIToLower(aNode.GetText(), lower);
534 nsCOMPtr<nsIAtom> name = do_GetAtom(lower);
535 nodeInfo = mNodeInfoManager->GetNodeInfo(name, nsnull, kNameSpaceID_XHTML);
537 else if (mNodeInfoCache[aNodeType]) {
538 nodeInfo = mNodeInfoCache[aNodeType];
540 else {
541 nsIParserService *parserService = nsContentUtils::GetParserService();
542 if (!parserService)
543 return nsnull;
545 nsIAtom *name = parserService->HTMLIdToAtomTag(aNodeType);
546 NS_ASSERTION(name, "What? Reverse mapping of id to string broken!!!");
548 nodeInfo = mNodeInfoManager->GetNodeInfo(name, nsnull, kNameSpaceID_XHTML);
549 NS_IF_ADDREF(mNodeInfoCache[aNodeType] = nodeInfo);
552 NS_ENSURE_TRUE(nodeInfo, nsnull);
554 // Make the content object
555 return CreateHTMLElement(aNodeType, nodeInfo, PR_TRUE);
558 nsresult
559 NS_NewHTMLElement(nsIContent** aResult, nsINodeInfo *aNodeInfo,
560 PRBool aFromParser)
562 *aResult = nsnull;
564 nsIParserService* parserService = nsContentUtils::GetParserService();
565 if (!parserService)
566 return NS_ERROR_OUT_OF_MEMORY;
568 nsIAtom *name = aNodeInfo->NameAtom();
570 NS_ASSERTION(aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML),
571 "Trying to HTML elements that don't have the XHTML namespace");
573 *aResult = CreateHTMLElement(parserService->
574 HTMLCaseSensitiveAtomTagToId(name),
575 aNodeInfo, aFromParser).get();
576 return *aResult ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
579 already_AddRefed<nsGenericHTMLElement>
580 CreateHTMLElement(PRUint32 aNodeType, nsINodeInfo *aNodeInfo,
581 PRBool aFromParser)
583 NS_ASSERTION(aNodeType <= NS_HTML_TAG_MAX ||
584 aNodeType == eHTMLTag_userdefined,
585 "aNodeType is out of bounds");
587 contentCreatorCallback cb = sContentCreatorCallbacks[aNodeType];
589 NS_ASSERTION(cb != NS_NewHTMLNOTUSEDElement,
590 "Don't know how to construct tag element!");
592 nsGenericHTMLElement* result = cb(aNodeInfo, aFromParser);
593 NS_IF_ADDREF(result);
595 return result;
598 //----------------------------------------------------------------------
600 SinkContext::SinkContext(HTMLContentSink* aSink)
601 : mSink(aSink),
602 mNotifyLevel(0),
603 mLastTextNodeSize(0),
604 mStack(nsnull),
605 mStackSize(0),
606 mStackPos(0),
607 mText(nsnull),
608 mTextLength(0),
609 mTextSize(0),
610 mLastTextCharWasCR(PR_FALSE)
612 MOZ_COUNT_CTOR(SinkContext);
615 SinkContext::~SinkContext()
617 MOZ_COUNT_DTOR(SinkContext);
619 if (mStack) {
620 for (PRInt32 i = 0; i < mStackPos; i++) {
621 NS_RELEASE(mStack[i].mContent);
623 delete [] mStack;
626 delete [] mText;
629 nsresult
630 SinkContext::Begin(nsHTMLTag aNodeType,
631 nsGenericHTMLElement* aRoot,
632 PRUint32 aNumFlushed,
633 PRInt32 aInsertionPoint)
635 if (mStackSize < 1) {
636 nsresult rv = GrowStack();
637 if (NS_FAILED(rv)) {
638 return rv;
642 mStack[0].mType = aNodeType;
643 mStack[0].mContent = aRoot;
644 mStack[0].mNumFlushed = aNumFlushed;
645 mStack[0].mInsertionPoint = aInsertionPoint;
646 NS_ADDREF(aRoot);
647 mStackPos = 1;
648 mTextLength = 0;
650 return NS_OK;
653 PRBool
654 SinkContext::IsCurrentContainer(nsHTMLTag aTag)
656 if (aTag == mStack[mStackPos - 1].mType) {
657 return PR_TRUE;
660 return PR_FALSE;
663 PRBool
664 SinkContext::IsAncestorContainer(nsHTMLTag aTag)
666 PRInt32 stackPos = mStackPos - 1;
668 while (stackPos >= 0) {
669 if (aTag == mStack[stackPos].mType) {
670 return PR_TRUE;
672 stackPos--;
675 return PR_FALSE;
678 void
679 SinkContext::DidAddContent(nsIContent* aContent)
681 if ((mStackPos == 2) && (mSink->mBody == mStack[1].mContent ||
682 mSink->mFrameset == mStack[1].mContent)) {
683 // We just finished adding something to the body
684 mNotifyLevel = 0;
687 // If we just added content to a node for which
688 // an insertion happen, we need to do an immediate
689 // notification for that insertion.
690 if (0 < mStackPos &&
691 mStack[mStackPos - 1].mInsertionPoint != -1 &&
692 mStack[mStackPos - 1].mNumFlushed <
693 mStack[mStackPos - 1].mContent->GetChildCount()) {
694 nsIContent* parent = mStack[mStackPos - 1].mContent;
696 #ifdef NS_DEBUG
697 // Tracing code
698 nsIParserService *parserService = nsContentUtils::GetParserService();
699 if (parserService) {
700 nsHTMLTag tag = nsHTMLTag(mStack[mStackPos - 1].mType);
701 NS_ConvertUTF16toUTF8 str(parserService->HTMLIdToStringTag(tag));
703 SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_REFLOW,
704 ("SinkContext::DidAddContent: Insertion notification for "
705 "parent=%s at position=%d and stackPos=%d",
706 str.get(), mStack[mStackPos - 1].mInsertionPoint - 1,
707 mStackPos - 1));
709 #endif
711 PRInt32 childIndex = mStack[mStackPos - 1].mInsertionPoint - 1;
712 NS_ASSERTION(parent->GetChildAt(childIndex) == aContent,
713 "Flushing the wrong child.");
714 mSink->NotifyInsert(parent, aContent, childIndex);
715 mStack[mStackPos - 1].mNumFlushed = parent->GetChildCount();
716 } else if (mSink->IsTimeToNotify()) {
717 SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_REFLOW,
718 ("SinkContext::DidAddContent: Notification as a result of the "
719 "interval expiring; backoff count: %d", mSink->mBackoffCount));
720 FlushTags();
724 nsresult
725 SinkContext::OpenContainer(const nsIParserNode& aNode)
727 FlushTextAndRelease();
729 SINK_TRACE_NODE(SINK_TRACE_CALLS,
730 "SinkContext::OpenContainer",
731 nsHTMLTag(aNode.GetNodeType()),
732 mStackPos,
733 mSink);
735 if (mStackPos <= 0) {
736 NS_ERROR("container w/o parent");
738 return NS_ERROR_FAILURE;
741 nsresult rv;
742 if (mStackPos + 1 > mStackSize) {
743 rv = GrowStack();
744 if (NS_FAILED(rv)) {
745 return rv;
749 // Create new container content object
750 nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
751 nsGenericHTMLElement* content =
752 mSink->CreateContentObject(aNode, nodeType).get();
753 if (!content) {
754 return NS_ERROR_OUT_OF_MEMORY;
757 mStack[mStackPos].mType = nodeType;
758 mStack[mStackPos].mContent = content;
759 mStack[mStackPos].mNumFlushed = 0;
760 mStack[mStackPos].mInsertionPoint = -1;
761 ++mStackPos;
763 // XXX Need to do this before we start adding attributes.
764 if (nodeType == eHTMLTag_style) {
765 nsCOMPtr<nsIStyleSheetLinkingElement> ssle = do_QueryInterface(content);
766 NS_ASSERTION(ssle, "Style content isn't a style sheet?");
767 ssle->SetLineNumber(aNode.GetSourceLineNumber());
769 // Now disable updates so that every time we add an attribute or child
770 // text token, we don't try to update the style sheet.
771 if (!mSink->mInsideNoXXXTag) {
772 ssle->InitStyleLinkElement(PR_FALSE);
774 else {
775 // We're not going to be evaluating this style anyway.
776 ssle->InitStyleLinkElement(PR_TRUE);
779 ssle->SetEnableUpdates(PR_FALSE);
782 rv = mSink->AddAttributes(aNode, content);
783 MaybeSetForm(content, nodeType, mSink);
785 mStack[mStackPos - 2].Add(content);
787 NS_ENSURE_SUCCESS(rv, rv);
789 if (mSink->IsMonolithicContainer(nodeType)) {
790 mSink->mInMonolithicContainer++;
793 // Special handling for certain tags
794 switch (nodeType) {
795 case eHTMLTag_form:
796 mSink->mCurrentForm = content;
797 break;
799 case eHTMLTag_frameset:
800 if (!mSink->mFrameset && mSink->mFramesEnabled) {
801 mSink->mFrameset = content;
803 break;
805 case eHTMLTag_noembed:
806 case eHTMLTag_noframes:
807 mSink->mInsideNoXXXTag++;
808 break;
810 case eHTMLTag_iframe:
811 mSink->mNumOpenIFRAMES++;
812 break;
814 case eHTMLTag_script:
816 nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(content);
817 NS_ASSERTION(sele, "Script content isn't a script element?");
818 sele->SetScriptLineNumber(aNode.GetSourceLineNumber());
820 break;
822 case eHTMLTag_button:
823 content->DoneCreatingElement();
824 break;
826 default:
827 break;
830 return NS_OK;
833 PRBool
834 SinkContext::HaveNotifiedForCurrentContent() const
836 if (0 < mStackPos) {
837 nsIContent* parent = mStack[mStackPos - 1].mContent;
838 return mStack[mStackPos-1].mNumFlushed == parent->GetChildCount();
841 return PR_TRUE;
844 nsIContent *
845 SinkContext::Node::Add(nsIContent *child)
847 NS_ASSERTION(mContent, "No parent to insert/append into!");
848 if (mInsertionPoint != -1) {
849 NS_ASSERTION(mNumFlushed == mContent->GetChildCount(),
850 "Inserting multiple children without flushing.");
851 mContent->InsertChildAt(child, mInsertionPoint++, PR_FALSE);
852 } else {
853 mContent->AppendChildTo(child, PR_FALSE);
855 return child;
858 nsresult
859 SinkContext::CloseContainer(const nsHTMLTag aTag, PRBool aMalformed)
861 nsresult result = NS_OK;
863 // Flush any collected text content. Release the last text
864 // node to indicate that no more should be added to it.
865 FlushTextAndRelease();
867 SINK_TRACE_NODE(SINK_TRACE_CALLS,
868 "SinkContext::CloseContainer",
869 aTag, mStackPos - 1, mSink);
871 NS_ASSERTION(mStackPos > 0,
872 "stack out of bounds. wrong context probably!");
874 if (mStackPos <= 0) {
875 return NS_OK; // Fix crash - Ref. bug 45975 or 45007
878 --mStackPos;
879 nsHTMLTag nodeType = mStack[mStackPos].mType;
881 NS_ASSERTION(nodeType == eHTMLTag_form || nodeType == aTag,
882 "Tag mismatch. Closing tag on wrong context or something?");
884 nsGenericHTMLElement* content = mStack[mStackPos].mContent;
886 content->Compact();
888 // If we're in a state where we do append notifications as
889 // we go up the tree, and we're at the level where the next
890 // notification needs to be done, do the notification.
891 if (mNotifyLevel >= mStackPos) {
892 // Check to see if new content has been added after our last
893 // notification
895 if (mStack[mStackPos].mNumFlushed < content->GetChildCount()) {
896 #ifdef NS_DEBUG
898 // Tracing code
899 SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_REFLOW,
900 ("SinkContext::CloseContainer: reflow on notifyImmediate "
901 "tag=%s newIndex=%d stackPos=%d",
902 nsAtomCString(mStack[mStackPos].mContent->Tag()).get(),
903 mStack[mStackPos].mNumFlushed, mStackPos));
905 #endif
906 mSink->NotifyAppend(content, mStack[mStackPos].mNumFlushed);
907 mStack[mStackPos].mNumFlushed = content->GetChildCount();
910 // Indicate that notification has now happened at this level
911 mNotifyLevel = mStackPos - 1;
914 if (mSink->IsMonolithicContainer(nodeType)) {
915 --mSink->mInMonolithicContainer;
918 DidAddContent(content);
920 // Special handling for certain tags
921 switch (nodeType) {
922 case eHTMLTag_noembed:
923 case eHTMLTag_noframes:
924 // Fix bug 40216
925 NS_ASSERTION((mSink->mInsideNoXXXTag > 0), "mInsideNoXXXTag underflow");
926 if (mSink->mInsideNoXXXTag > 0) {
927 mSink->mInsideNoXXXTag--;
930 break;
931 case eHTMLTag_form:
933 mSink->mFormOnStack = PR_FALSE;
934 // If there's a FORM on the stack, but this close tag doesn't
935 // close the form, then close out the form *and* close out the
936 // next container up. This is since the parser doesn't do fix up
937 // of invalid form nesting. When the end FORM tag comes through,
938 // we'll ignore it.
939 if (aTag != nodeType) {
940 result = CloseContainer(aTag, PR_FALSE);
944 break;
945 case eHTMLTag_iframe:
946 mSink->mNumOpenIFRAMES--;
948 break;
950 #ifdef MOZ_MEDIA
951 case eHTMLTag_video:
952 case eHTMLTag_audio:
953 #endif
954 case eHTMLTag_select:
955 case eHTMLTag_textarea:
956 case eHTMLTag_object:
957 case eHTMLTag_applet:
958 case eHTMLTag_title:
959 content->DoneAddingChildren(HaveNotifiedForCurrentContent());
960 break;
962 case eHTMLTag_script:
963 result = mSink->ProcessSCRIPTEndTag(content,
964 aMalformed);
965 break;
967 case eHTMLTag_style:
968 result = mSink->ProcessSTYLEEndTag(content);
969 break;
971 default:
972 break;
975 NS_IF_RELEASE(content);
977 #ifdef DEBUG
978 if (SINK_LOG_TEST(gSinkLogModuleInfo, SINK_ALWAYS_REFLOW)) {
979 mSink->ForceReflow();
981 #endif
983 return result;
986 nsresult
987 SinkContext::AddLeaf(const nsIParserNode& aNode)
989 SINK_TRACE_NODE(SINK_TRACE_CALLS,
990 "SinkContext::AddLeaf",
991 nsHTMLTag(aNode.GetNodeType()),
992 mStackPos, mSink);
994 nsresult rv = NS_OK;
996 switch (aNode.GetTokenType()) {
997 case eToken_start:
999 FlushTextAndRelease();
1001 // Create new leaf content object
1002 nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
1003 nsRefPtr<nsGenericHTMLElement> content =
1004 mSink->CreateContentObject(aNode, nodeType);
1005 NS_ENSURE_TRUE(content, NS_ERROR_OUT_OF_MEMORY);
1007 if (nodeType == eHTMLTag_form) {
1008 mSink->mCurrentForm = content;
1011 rv = mSink->AddAttributes(aNode, content);
1013 NS_ENSURE_SUCCESS(rv, rv);
1015 MaybeSetForm(content, nodeType, mSink);
1017 // Add new leaf to its parent
1018 AddLeaf(content);
1020 // Additional processing needed once the element is in the tree
1021 switch (nodeType) {
1022 case eHTMLTag_meta:
1023 // XXX It's just not sufficient to check if the parent is head. Also
1024 // check for the preference.
1025 // Bug 40072: Don't evaluate METAs after FRAMESET.
1026 if (!mSink->mInsideNoXXXTag && !mSink->mFrameset) {
1027 rv = mSink->ProcessMETATag(content);
1029 break;
1031 case eHTMLTag_input:
1032 content->DoneCreatingElement();
1034 break;
1036 default:
1037 break;
1040 break;
1042 case eToken_text:
1043 case eToken_whitespace:
1044 case eToken_newline:
1045 rv = AddText(aNode.GetText());
1047 break;
1048 case eToken_entity:
1050 nsAutoString tmp;
1051 PRInt32 unicode = aNode.TranslateToUnicodeStr(tmp);
1052 if (unicode < 0) {
1053 rv = AddText(aNode.GetText());
1054 } else {
1055 // Map carriage returns to newlines
1056 if (!tmp.IsEmpty()) {
1057 if (tmp.CharAt(0) == '\r') {
1058 tmp.Assign((PRUnichar)'\n');
1060 rv = AddText(tmp);
1065 break;
1066 default:
1067 break;
1070 return rv;
1073 nsresult
1074 SinkContext::AddLeaf(nsIContent* aContent)
1076 NS_ASSERTION(mStackPos > 0, "leaf w/o container");
1077 if (mStackPos <= 0) {
1078 return NS_ERROR_FAILURE;
1081 DidAddContent(mStack[mStackPos - 1].Add(aContent));
1083 #ifdef DEBUG
1084 if (SINK_LOG_TEST(gSinkLogModuleInfo, SINK_ALWAYS_REFLOW)) {
1085 mSink->ForceReflow();
1087 #endif
1089 return NS_OK;
1092 nsresult
1093 SinkContext::AddComment(const nsIParserNode& aNode)
1095 SINK_TRACE_NODE(SINK_TRACE_CALLS,
1096 "SinkContext::AddLeaf",
1097 nsHTMLTag(aNode.GetNodeType()),
1098 mStackPos, mSink);
1099 FlushTextAndRelease();
1101 if (!mSink) {
1102 return NS_ERROR_UNEXPECTED;
1105 nsCOMPtr<nsIContent> comment;
1106 nsresult rv = NS_NewCommentNode(getter_AddRefs(comment),
1107 mSink->mNodeInfoManager);
1108 NS_ENSURE_SUCCESS(rv, rv);
1110 comment->SetText(aNode.GetText(), PR_FALSE);
1112 NS_ASSERTION(mStackPos > 0, "stack out of bounds");
1113 if (mStackPos <= 0) {
1114 return NS_ERROR_FAILURE;
1118 Node &parentNode = mStack[mStackPos - 1];
1119 nsGenericHTMLElement *parent = parentNode.mContent;
1120 if (!mSink->mBody && !mSink->mFrameset && mSink->mHead)
1121 // XXXbz but this will make DidAddContent use the wrong parent for
1122 // the notification! That seems so bogus it's not even funny.
1123 parentNode.mContent = mSink->mHead;
1124 DidAddContent(parentNode.Add(comment));
1125 parentNode.mContent = parent;
1128 #ifdef DEBUG
1129 if (SINK_LOG_TEST(gSinkLogModuleInfo, SINK_ALWAYS_REFLOW)) {
1130 mSink->ForceReflow();
1132 #endif
1134 return rv;
1137 nsresult
1138 SinkContext::End()
1140 for (PRInt32 i = 0; i < mStackPos; i++) {
1141 NS_RELEASE(mStack[i].mContent);
1144 mStackPos = 0;
1145 mTextLength = 0;
1147 return NS_OK;
1150 nsresult
1151 SinkContext::GrowStack()
1153 PRInt32 newSize = mStackSize * 2;
1154 if (newSize == 0) {
1155 newSize = 32;
1158 Node* stack = new Node[newSize];
1159 if (!stack) {
1160 return NS_ERROR_OUT_OF_MEMORY;
1163 if (mStackPos != 0) {
1164 memcpy(stack, mStack, sizeof(Node) * mStackPos);
1165 delete [] mStack;
1168 mStack = stack;
1169 mStackSize = newSize;
1171 return NS_OK;
1175 * Add textual content to the current running text buffer. If the text buffer
1176 * overflows, flush out the text by creating a text content object and adding
1177 * it to the content tree.
1180 // XXX If we get a giant string grow the buffer instead of chopping it
1181 // up???
1182 nsresult
1183 SinkContext::AddText(const nsAString& aText)
1185 PRInt32 addLen = aText.Length();
1186 if (addLen == 0) {
1187 return NS_OK;
1190 // Create buffer when we first need it
1191 if (mTextSize == 0) {
1192 mText = new PRUnichar[4096];
1193 if (!mText) {
1194 return NS_ERROR_OUT_OF_MEMORY;
1196 mTextSize = 4096;
1199 // Copy data from string into our buffer; flush buffer when it fills up
1200 PRInt32 offset = 0;
1202 while (addLen != 0) {
1203 PRInt32 amount = mTextSize - mTextLength;
1205 if (amount > addLen) {
1206 amount = addLen;
1209 if (amount == 0) {
1210 // Don't release last text node so we can add to it again
1211 nsresult rv = FlushText();
1212 if (NS_FAILED(rv)) {
1213 return rv;
1216 // Go back to the top of the loop so we re-calculate amount and
1217 // don't fall through to CopyNewlineNormalizedUnicodeTo with a
1218 // zero-length amount (which invalidates mLastTextCharWasCR).
1219 continue;
1222 mTextLength +=
1223 nsContentUtils::CopyNewlineNormalizedUnicodeTo(aText, offset,
1224 &mText[mTextLength],
1225 amount,
1226 mLastTextCharWasCR);
1227 offset += amount;
1228 addLen -= amount;
1231 return NS_OK;
1235 * NOTE!! Forked into nsXMLContentSink. Please keep in sync.
1237 * Flush all elements that have been seen so far such that
1238 * they are visible in the tree. Specifically, make sure
1239 * that they are all added to their respective parents.
1240 * Also, do notification at the top for all content that
1241 * has been newly added so that the frame tree is complete.
1243 nsresult
1244 SinkContext::FlushTags()
1246 mSink->mDeferredFlushTags = PR_FALSE;
1247 PRBool oldBeganUpdate = mSink->mBeganUpdate;
1248 PRUint32 oldUpdates = mSink->mUpdatesInNotification;
1250 ++(mSink->mInNotification);
1251 mSink->mUpdatesInNotification = 0;
1253 // Scope so we call EndUpdate before we decrease mInNotification
1254 mozAutoDocUpdate updateBatch(mSink->mDocument, UPDATE_CONTENT_MODEL,
1255 PR_TRUE);
1256 mSink->mBeganUpdate = PR_TRUE;
1258 // Don't release last text node in case we need to add to it again
1259 FlushText();
1261 // Start from the base of the stack (growing downward) and do
1262 // a notification from the node that is closest to the root of
1263 // tree for any content that has been added.
1265 // Note that we can start at stackPos == 0 here, because it's the caller's
1266 // responsibility to handle flushing interactions between contexts (see
1267 // HTMLContentSink::BeginContext).
1268 PRInt32 stackPos = 0;
1269 PRBool flushed = PR_FALSE;
1270 PRUint32 childCount;
1271 nsGenericHTMLElement* content;
1273 while (stackPos < mStackPos) {
1274 content = mStack[stackPos].mContent;
1275 childCount = content->GetChildCount();
1277 if (!flushed && (mStack[stackPos].mNumFlushed < childCount)) {
1278 #ifdef NS_DEBUG
1280 // Tracing code
1281 SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_REFLOW,
1282 ("SinkContext::FlushTags: tag=%s from newindex=%d at "
1283 "stackPos=%d",
1284 nsAtomCString(mStack[stackPos].mContent->Tag()).get(),
1285 mStack[stackPos].mNumFlushed, stackPos));
1287 #endif
1288 if (mStack[stackPos].mInsertionPoint != -1) {
1289 // We might have popped the child off our stack already
1290 // but not notified on it yet, which is why we have to get it
1291 // directly from its parent node.
1293 PRInt32 childIndex = mStack[stackPos].mInsertionPoint - 1;
1294 nsIContent* child = content->GetChildAt(childIndex);
1295 // Child not on stack anymore; can't assert it's correct
1296 NS_ASSERTION(!(mStackPos > (stackPos + 1)) ||
1297 (child == mStack[stackPos + 1].mContent),
1298 "Flushing the wrong child.");
1299 mSink->NotifyInsert(content, child, childIndex);
1300 } else {
1301 mSink->NotifyAppend(content, mStack[stackPos].mNumFlushed);
1304 flushed = PR_TRUE;
1307 mStack[stackPos].mNumFlushed = childCount;
1308 stackPos++;
1310 mNotifyLevel = mStackPos - 1;
1312 --(mSink->mInNotification);
1314 if (mSink->mUpdatesInNotification > 1) {
1315 UpdateChildCounts();
1318 mSink->mUpdatesInNotification = oldUpdates;
1319 mSink->mBeganUpdate = oldBeganUpdate;
1321 return NS_OK;
1325 * NOTE!! Forked into nsXMLContentSink. Please keep in sync.
1327 void
1328 SinkContext::UpdateChildCounts()
1330 // Start from the top of the stack (growing upwards) and see if any
1331 // new content has been appended. If so, we recognize that reflows
1332 // have been generated for it and we should make sure that no
1333 // further reflows occur. Note that we have to include stackPos == 0
1334 // to properly notify on kids of <html>.
1335 PRInt32 stackPos = mStackPos - 1;
1336 while (stackPos >= 0) {
1337 Node & node = mStack[stackPos];
1338 node.mNumFlushed = node.mContent->GetChildCount();
1340 stackPos--;
1343 mNotifyLevel = mStackPos - 1;
1347 * Flush any buffered text out by creating a text content object and
1348 * adding it to the content.
1350 nsresult
1351 SinkContext::FlushText(PRBool* aDidFlush, PRBool aReleaseLast)
1353 nsresult rv = NS_OK;
1354 PRBool didFlush = PR_FALSE;
1356 if (mTextLength != 0) {
1357 if (mLastTextNode) {
1358 if ((mLastTextNodeSize + mTextLength) > mSink->mMaxTextRun) {
1359 mLastTextNodeSize = 0;
1360 mLastTextNode = nsnull;
1361 FlushText(aDidFlush, aReleaseLast);
1362 } else {
1363 PRBool notify = HaveNotifiedForCurrentContent();
1364 // We could probably always increase mInNotification here since
1365 // if AppendText doesn't notify it shouldn't trigger evil code.
1366 // But just in case it does, we don't want to mask any notifications.
1367 if (notify) {
1368 ++mSink->mInNotification;
1370 rv = mLastTextNode->AppendText(mText, mTextLength, notify);
1371 if (notify) {
1372 --mSink->mInNotification;
1375 mLastTextNodeSize += mTextLength;
1376 mTextLength = 0;
1377 didFlush = PR_TRUE;
1379 } else {
1380 nsCOMPtr<nsIContent> textContent;
1381 rv = NS_NewTextNode(getter_AddRefs(textContent),
1382 mSink->mNodeInfoManager);
1383 NS_ENSURE_SUCCESS(rv, rv);
1385 mLastTextNode = textContent;
1387 // Set the text in the text node
1388 mLastTextNode->SetText(mText, mTextLength, PR_FALSE);
1390 // Eat up the rest of the text up in state.
1391 mLastTextNodeSize += mTextLength;
1392 mTextLength = 0;
1394 rv = AddLeaf(mLastTextNode);
1395 NS_ENSURE_SUCCESS(rv, rv);
1397 didFlush = PR_TRUE;
1401 if (aDidFlush) {
1402 *aDidFlush = didFlush;
1405 if (aReleaseLast) {
1406 mLastTextNodeSize = 0;
1407 mLastTextNode = nsnull;
1408 mLastTextCharWasCR = PR_FALSE;
1411 #ifdef DEBUG
1412 if (didFlush &&
1413 SINK_LOG_TEST(gSinkLogModuleInfo, SINK_ALWAYS_REFLOW)) {
1414 mSink->ForceReflow();
1416 #endif
1418 return rv;
1422 nsresult
1423 NS_NewHTMLContentSink(nsIHTMLContentSink** aResult,
1424 nsIDocument* aDoc,
1425 nsIURI* aURI,
1426 nsISupports* aContainer,
1427 nsIChannel* aChannel)
1429 NS_ENSURE_ARG_POINTER(aResult);
1431 nsRefPtr<HTMLContentSink> it = new HTMLContentSink();
1433 if (!it) {
1434 return NS_ERROR_OUT_OF_MEMORY;
1437 nsresult rv = it->Init(aDoc, aURI, aContainer, aChannel);
1439 NS_ENSURE_SUCCESS(rv, rv);
1441 *aResult = it;
1442 NS_ADDREF(*aResult);
1444 return NS_OK;
1447 HTMLContentSink::HTMLContentSink()
1449 // Note: operator new zeros our memory
1452 #ifdef NS_DEBUG
1453 if (!gSinkLogModuleInfo) {
1454 gSinkLogModuleInfo = PR_NewLogModule("htmlcontentsink");
1456 #endif
1459 HTMLContentSink::~HTMLContentSink()
1461 if (mNotificationTimer) {
1462 mNotificationTimer->Cancel();
1465 PRInt32 numContexts = mContextStack.Length();
1467 if (mCurrentContext == mHeadContext && numContexts > 0) {
1468 // Pop off the second html context if it's not done earlier
1469 mContextStack.RemoveElementAt(--numContexts);
1472 PRInt32 i;
1473 for (i = 0; i < numContexts; i++) {
1474 SinkContext* sc = mContextStack.ElementAt(i);
1475 if (sc) {
1476 sc->End();
1477 if (sc == mCurrentContext) {
1478 mCurrentContext = nsnull;
1481 delete sc;
1485 if (mCurrentContext == mHeadContext) {
1486 mCurrentContext = nsnull;
1489 delete mCurrentContext;
1491 delete mHeadContext;
1493 for (i = 0; PRUint32(i) < NS_ARRAY_LENGTH(mNodeInfoCache); ++i) {
1494 NS_IF_RELEASE(mNodeInfoCache[i]);
1498 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLContentSink)
1500 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLContentSink, nsContentSink)
1501 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mHTMLDocument)
1502 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRoot)
1503 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mBody)
1504 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFrameset)
1505 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mHead)
1506 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCurrentForm)
1507 for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(tmp->mNodeInfoCache); ++i) {
1508 NS_IF_RELEASE(tmp->mNodeInfoCache[i]);
1510 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1511 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLContentSink,
1512 nsContentSink)
1513 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mHTMLDocument)
1514 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mRoot)
1515 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mBody)
1516 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFrameset)
1517 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mHead)
1518 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCurrentForm)
1519 for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(tmp->mNodeInfoCache); ++i) {
1520 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mNodeInfoCache[i]");
1521 cb.NoteXPCOMChild(tmp->mNodeInfoCache[i]);
1523 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1525 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLContentSink)
1526 NS_INTERFACE_TABLE_BEGIN
1527 NS_INTERFACE_TABLE_ENTRY(HTMLContentSink, nsIContentSink)
1528 NS_INTERFACE_TABLE_ENTRY(HTMLContentSink, nsIHTMLContentSink)
1529 #if DEBUG
1530 NS_INTERFACE_TABLE_ENTRY(HTMLContentSink, nsIDebugDumpContent)
1531 #endif
1532 NS_INTERFACE_TABLE_END
1533 NS_INTERFACE_TABLE_TAIL_INHERITING(nsContentSink)
1535 NS_IMPL_ADDREF_INHERITED(HTMLContentSink, nsContentSink)
1536 NS_IMPL_RELEASE_INHERITED(HTMLContentSink, nsContentSink)
1538 static PRBool
1539 IsScriptEnabled(nsIDocument *aDoc, nsIDocShell *aContainer)
1541 NS_ENSURE_TRUE(aDoc && aContainer, PR_TRUE);
1543 nsCOMPtr<nsIScriptGlobalObject> globalObject = aDoc->GetScriptGlobalObject();
1545 // Getting context is tricky if the document hasn't had its
1546 // GlobalObject set yet
1547 if (!globalObject) {
1548 nsCOMPtr<nsIScriptGlobalObjectOwner> owner = do_GetInterface(aContainer);
1549 NS_ENSURE_TRUE(owner, PR_TRUE);
1551 globalObject = owner->GetScriptGlobalObject();
1552 NS_ENSURE_TRUE(globalObject, PR_TRUE);
1555 nsIScriptContext *scriptContext = globalObject->GetContext();
1556 NS_ENSURE_TRUE(scriptContext, PR_TRUE);
1558 JSContext* cx = (JSContext *) scriptContext->GetNativeContext();
1559 NS_ENSURE_TRUE(cx, PR_TRUE);
1561 PRBool enabled = PR_TRUE;
1562 nsContentUtils::GetSecurityManager()->
1563 CanExecuteScripts(cx, aDoc->NodePrincipal(), &enabled);
1564 return enabled;
1567 nsresult
1568 HTMLContentSink::Init(nsIDocument* aDoc,
1569 nsIURI* aURI,
1570 nsISupports* aContainer,
1571 nsIChannel* aChannel)
1573 NS_ENSURE_TRUE(aContainer, NS_ERROR_NULL_POINTER);
1575 nsresult rv = nsContentSink::Init(aDoc, aURI, aContainer, aChannel);
1576 if (NS_FAILED(rv)) {
1577 return rv;
1580 aDoc->AddObserver(this);
1581 mIsDocumentObserver = PR_TRUE;
1582 mHTMLDocument = do_QueryInterface(aDoc);
1584 mObservers = nsnull;
1585 nsIParserService* service = nsContentUtils::GetParserService();
1586 if (!service) {
1587 return NS_ERROR_OUT_OF_MEMORY;
1590 service->GetTopicObservers(NS_LITERAL_STRING("text/html"),
1591 getter_AddRefs(mObservers));
1593 NS_ASSERTION(mDocShell, "oops no docshell!");
1595 // Find out if subframes are enabled
1596 if (mDocShell) {
1597 PRBool subFramesEnabled = PR_TRUE;
1598 mDocShell->GetAllowSubframes(&subFramesEnabled);
1599 if (subFramesEnabled) {
1600 mFramesEnabled = PR_TRUE;
1604 // Find out if scripts are enabled, if not, show <noscript> content
1605 if (IsScriptEnabled(aDoc, mDocShell)) {
1606 mScriptEnabled = PR_TRUE;
1610 // Changed from 8192 to greatly improve page loading performance on
1611 // large pages. See bugzilla bug 77540.
1612 mMaxTextRun = nsContentUtils::GetIntPref("content.maxtextrun", 8191);
1614 nsCOMPtr<nsINodeInfo> nodeInfo;
1615 nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::html, nsnull,
1616 kNameSpaceID_XHTML);
1617 NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
1619 // Make root part
1620 mRoot = NS_NewHTMLHtmlElement(nodeInfo);
1621 if (!mRoot) {
1622 return NS_ERROR_OUT_OF_MEMORY;
1625 NS_ASSERTION(mDocument->GetChildCount() == 0,
1626 "Document should have no kids here!");
1627 rv = mDocument->AppendChildTo(mRoot, PR_FALSE);
1628 NS_ENSURE_SUCCESS(rv, rv);
1630 // Make head part
1631 nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::head,
1632 nsnull, kNameSpaceID_XHTML);
1633 NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
1635 mHead = NS_NewHTMLHeadElement(nodeInfo);
1636 if (NS_FAILED(rv)) {
1637 return NS_ERROR_OUT_OF_MEMORY;
1640 mRoot->AppendChildTo(mHead, PR_FALSE);
1642 mCurrentContext = new SinkContext(this);
1643 NS_ENSURE_TRUE(mCurrentContext, NS_ERROR_OUT_OF_MEMORY);
1644 mCurrentContext->Begin(eHTMLTag_html, mRoot, 0, -1);
1645 mContextStack.AppendElement(mCurrentContext);
1647 #ifdef NS_DEBUG
1648 nsCAutoString spec;
1649 (void)aURI->GetSpec(spec);
1650 SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_CALLS,
1651 ("HTMLContentSink::Init: this=%p url='%s'",
1652 this, spec.get()));
1653 #endif
1654 return NS_OK;
1657 NS_IMETHODIMP
1658 HTMLContentSink::WillParse(void)
1660 return WillParseImpl();
1663 NS_IMETHODIMP
1664 HTMLContentSink::WillBuildModel(nsDTDMode aDTDMode)
1666 WillBuildModelImpl();
1668 if (mHTMLDocument) {
1669 nsCompatibility mode = eCompatibility_NavQuirks;
1670 switch (aDTDMode) {
1671 case eDTDMode_full_standards:
1672 mode = eCompatibility_FullStandards;
1673 break;
1674 case eDTDMode_almost_standards:
1675 mode = eCompatibility_AlmostStandards;
1676 break;
1677 default:
1678 break;
1680 mHTMLDocument->SetCompatibilityMode(mode);
1683 // Notify document that the load is beginning
1684 mDocument->BeginLoad();
1686 return NS_OK;
1689 NS_IMETHODIMP
1690 HTMLContentSink::DidBuildModel(PRBool aTerminated)
1692 DidBuildModelImpl(aTerminated);
1694 // Reflow the last batch of content
1695 if (mBody || mFrameset) {
1696 SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_REFLOW,
1697 ("HTMLContentSink::DidBuildModel: layout final content"));
1698 mCurrentContext->FlushTags();
1699 } else if (!mLayoutStarted) {
1700 // We never saw the body, and layout never got started. Force
1701 // layout *now*, to get an initial reflow.
1702 SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_REFLOW,
1703 ("HTMLContentSink::DidBuildModel: forcing reflow on empty "
1704 "document"));
1706 // NOTE: only force the layout if we are NOT destroying the
1707 // docshell. If we are destroying it, then starting layout will
1708 // likely cause us to crash, or at best waste a lot of time as we
1709 // are just going to tear it down anyway.
1710 PRBool bDestroying = PR_TRUE;
1711 if (mDocShell) {
1712 mDocShell->IsBeingDestroyed(&bDestroying);
1715 if (!bDestroying) {
1716 StartLayout(PR_FALSE);
1720 ScrollToRef();
1722 mDocument->ScriptLoader()->RemoveObserver(this);
1724 // Make sure we no longer respond to document mutations. We've flushed all
1725 // our notifications out, so there's no need to do anything else here.
1727 // XXXbz I wonder whether we could End() our contexts here too, or something,
1728 // just to make sure we no longer notify... Or is the mIsDocumentObserver
1729 // thing sufficient?
1730 mDocument->RemoveObserver(this);
1731 mIsDocumentObserver = PR_FALSE;
1733 mDocument->EndLoad();
1735 DropParserAndPerfHint();
1737 return NS_OK;
1740 NS_IMETHODIMP
1741 HTMLContentSink::SetParser(nsIParser* aParser)
1743 NS_PRECONDITION(aParser, "Should have a parser here!");
1744 mParser = aParser;
1745 return NS_OK;
1748 NS_IMETHODIMP_(PRBool)
1749 HTMLContentSink::IsFormOnStack()
1751 return mFormOnStack;
1754 NS_IMETHODIMP
1755 HTMLContentSink::BeginContext(PRInt32 aPosition)
1757 NS_PRECONDITION(aPosition > -1, "out of bounds");
1759 // Create new context
1760 SinkContext* sc = new SinkContext(this);
1761 if (!sc) {
1762 return NS_ERROR_OUT_OF_MEMORY;
1765 if (!mCurrentContext) {
1766 NS_ERROR("Nonexistent context");
1768 return NS_ERROR_FAILURE;
1771 // Flush everything in the current context so that we don't have
1772 // to worry about insertions resulting in inconsistent frame creation.
1773 mCurrentContext->FlushTags();
1775 // Sanity check.
1776 if (mCurrentContext->mStackPos <= aPosition) {
1777 NS_ERROR("Out of bounds position");
1778 return NS_ERROR_FAILURE;
1781 PRInt32 insertionPoint = -1;
1782 nsHTMLTag nodeType = mCurrentContext->mStack[aPosition].mType;
1783 nsGenericHTMLElement* content = mCurrentContext->mStack[aPosition].mContent;
1785 // If the content under which the new context is created
1786 // has a child on the stack, the insertion point is
1787 // before the last child.
1788 if (aPosition < (mCurrentContext->mStackPos - 1)) {
1789 insertionPoint = content->GetChildCount() - 1;
1792 sc->Begin(nodeType,
1793 content,
1794 mCurrentContext->mStack[aPosition].mNumFlushed,
1795 insertionPoint);
1796 NS_ADDREF(sc->mSink);
1798 mContextStack.AppendElement(mCurrentContext);
1799 mCurrentContext = sc;
1800 return NS_OK;
1803 NS_IMETHODIMP
1804 HTMLContentSink::EndContext(PRInt32 aPosition)
1806 NS_PRECONDITION(mCurrentContext && aPosition > -1, "nonexistent context");
1808 PRUint32 n = mContextStack.Length() - 1;
1809 SinkContext* sc = mContextStack.ElementAt(n);
1811 const SinkContext::Node &bottom = mCurrentContext->mStack[0];
1813 NS_ASSERTION(sc->mStack[aPosition].mType == bottom.mType,
1814 "ending a wrong context");
1816 mCurrentContext->FlushTextAndRelease();
1818 NS_ASSERTION(bottom.mContent->GetChildCount() == bottom.mNumFlushed,
1819 "Node at base of context stack not fully flushed.");
1821 // Flushing tags before the assertion on the previous line would
1822 // undoubtedly prevent the assertion from failing, but it shouldn't
1823 // be failing anyway, FlushTags or no. Flushing here is nevertheless
1824 // a worthwhile precaution, since we lose some information (e.g.,
1825 // mInsertionPoints) when we end the current context.
1826 mCurrentContext->FlushTags();
1828 sc->mStack[aPosition].mNumFlushed = bottom.mNumFlushed;
1830 for (PRInt32 i = 0; i<mCurrentContext->mStackPos; i++) {
1831 NS_IF_RELEASE(mCurrentContext->mStack[i].mContent);
1834 delete [] mCurrentContext->mStack;
1836 mCurrentContext->mStack = nsnull;
1837 mCurrentContext->mStackPos = 0;
1838 mCurrentContext->mStackSize = 0;
1840 delete [] mCurrentContext->mText;
1842 mCurrentContext->mText = nsnull;
1843 mCurrentContext->mTextLength = 0;
1844 mCurrentContext->mTextSize = 0;
1846 NS_IF_RELEASE(mCurrentContext->mSink);
1848 delete mCurrentContext;
1850 mCurrentContext = sc;
1851 mContextStack.RemoveElementAt(n);
1852 return NS_OK;
1855 nsresult
1856 HTMLContentSink::CloseHTML()
1858 SINK_TRACE_NODE(SINK_TRACE_CALLS,
1859 "HTMLContentSink::CloseHTML",
1860 eHTMLTag_html, 0, this);
1862 if (mHeadContext) {
1863 if (mCurrentContext == mHeadContext) {
1864 PRUint32 numContexts = mContextStack.Length();
1866 // Pop off the second html context if it's not done earlier
1867 mCurrentContext = mContextStack.ElementAt(--numContexts);
1868 mContextStack.RemoveElementAt(numContexts);
1871 NS_ASSERTION(mHeadContext->mTextLength == 0, "Losing text");
1873 mHeadContext->End();
1875 delete mHeadContext;
1876 mHeadContext = nsnull;
1879 return NS_OK;
1882 nsresult
1883 HTMLContentSink::OpenHead()
1885 nsresult rv = OpenHeadContext();
1886 return rv;
1889 nsresult
1890 HTMLContentSink::OpenBody(const nsIParserNode& aNode)
1892 SINK_TRACE_NODE(SINK_TRACE_CALLS,
1893 "HTMLContentSink::OpenBody",
1894 eHTMLTag_body,
1895 mCurrentContext->mStackPos,
1896 this);
1898 CloseHeadContext(); // do this just in case if the HEAD was left open!
1900 // Add attributes, if any, to the current BODY node
1901 if (mBody) {
1902 AddAttributes(aNode, mBody, PR_TRUE, PR_TRUE);
1903 return NS_OK;
1906 nsresult rv = mCurrentContext->OpenContainer(aNode);
1908 if (NS_FAILED(rv)) {
1909 return rv;
1912 mBody = mCurrentContext->mStack[mCurrentContext->mStackPos - 1].mContent;
1914 if (mCurrentContext->mStackPos > 1) {
1915 PRInt32 parentIndex = mCurrentContext->mStackPos - 2;
1916 nsGenericHTMLElement *parent = mCurrentContext->mStack[parentIndex].mContent;
1917 PRInt32 numFlushed = mCurrentContext->mStack[parentIndex].mNumFlushed;
1918 PRInt32 childCount = parent->GetChildCount();
1919 NS_ASSERTION(numFlushed < childCount, "Already notified on the body?");
1921 PRInt32 insertionPoint =
1922 mCurrentContext->mStack[parentIndex].mInsertionPoint;
1924 // XXX: I have yet to see a case where numFlushed is non-zero and
1925 // insertionPoint is not -1, but this code will try to handle
1926 // those cases too.
1928 PRUint32 oldUpdates = mUpdatesInNotification;
1929 mUpdatesInNotification = 0;
1930 if (insertionPoint != -1) {
1931 NotifyInsert(parent, mBody, insertionPoint - 1);
1932 } else {
1933 NotifyAppend(parent, numFlushed);
1935 mCurrentContext->mStack[parentIndex].mNumFlushed = childCount;
1936 if (mUpdatesInNotification > 1) {
1937 UpdateChildCounts();
1939 mUpdatesInNotification = oldUpdates;
1942 StartLayout(PR_FALSE);
1944 return NS_OK;
1947 nsresult
1948 HTMLContentSink::CloseBody()
1950 SINK_TRACE_NODE(SINK_TRACE_CALLS,
1951 "HTMLContentSink::CloseBody",
1952 eHTMLTag_body,
1953 mCurrentContext->mStackPos - 1,
1954 this);
1956 PRBool didFlush;
1957 nsresult rv = mCurrentContext->FlushTextAndRelease(&didFlush);
1958 if (NS_FAILED(rv)) {
1959 return rv;
1962 // Flush out anything that's left
1963 SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_REFLOW,
1964 ("HTMLContentSink::CloseBody: layout final body content"));
1966 mCurrentContext->FlushTags();
1967 mCurrentContext->CloseContainer(eHTMLTag_body, PR_FALSE);
1969 return NS_OK;
1972 nsresult
1973 HTMLContentSink::OpenForm(const nsIParserNode& aNode)
1975 nsresult result = NS_OK;
1977 mCurrentContext->FlushTextAndRelease();
1979 SINK_TRACE_NODE(SINK_TRACE_CALLS,
1980 "HTMLContentSink::OpenForm",
1981 eHTMLTag_form,
1982 mCurrentContext->mStackPos,
1983 this);
1985 // Close out previous form if it's there. If there is one
1986 // around, it's probably because the last one wasn't well-formed.
1987 mCurrentForm = nsnull;
1989 // Check if the parent is a table, tbody, thead, tfoot, tr, col or
1990 // colgroup. If so, we fix up by making the form leaf content.
1991 if (mCurrentContext->IsCurrentContainer(eHTMLTag_table) ||
1992 mCurrentContext->IsCurrentContainer(eHTMLTag_tbody) ||
1993 mCurrentContext->IsCurrentContainer(eHTMLTag_thead) ||
1994 mCurrentContext->IsCurrentContainer(eHTMLTag_tfoot) ||
1995 mCurrentContext->IsCurrentContainer(eHTMLTag_tr) ||
1996 mCurrentContext->IsCurrentContainer(eHTMLTag_col) ||
1997 mCurrentContext->IsCurrentContainer(eHTMLTag_colgroup)) {
1998 result = mCurrentContext->AddLeaf(aNode);
1999 } else {
2000 mFormOnStack = PR_TRUE;
2001 // Otherwise the form can be a content parent.
2002 result = mCurrentContext->OpenContainer(aNode);
2005 return result;
2008 nsresult
2009 HTMLContentSink::CloseForm()
2011 nsresult result = NS_OK;
2013 SINK_TRACE_NODE(SINK_TRACE_CALLS,
2014 "HTMLContentSink::CloseForm",
2015 eHTMLTag_form,
2016 mCurrentContext->mStackPos - 1,
2017 this);
2019 if (mCurrentForm) {
2020 // if this is a well-formed form, close it too
2021 if (mCurrentContext->IsCurrentContainer(eHTMLTag_form)) {
2022 result = mCurrentContext->CloseContainer(eHTMLTag_form, PR_FALSE);
2023 mFormOnStack = PR_FALSE;
2026 mCurrentForm = nsnull;
2029 return result;
2032 nsresult
2033 HTMLContentSink::OpenFrameset(const nsIParserNode& aNode)
2035 SINK_TRACE_NODE(SINK_TRACE_CALLS,
2036 "HTMLContentSink::OpenFrameset",
2037 eHTMLTag_frameset,
2038 mCurrentContext->mStackPos,
2039 this);
2041 CloseHeadContext(); // do this just in case if the HEAD was left open!
2043 // Need to keep track of whether OpenContainer changes mFrameset
2044 nsGenericHTMLElement* oldFrameset = mFrameset;
2045 nsresult rv = mCurrentContext->OpenContainer(aNode);
2046 PRBool isFirstFrameset = NS_SUCCEEDED(rv) && mFrameset != oldFrameset;
2048 if (isFirstFrameset && mCurrentContext->mStackPos > 1) {
2049 NS_ASSERTION(mFrameset, "Must have frameset!");
2050 // Have to notify for the frameset now, since we never actually
2051 // close out <html>, so won't notify for it then.
2052 PRInt32 parentIndex = mCurrentContext->mStackPos - 2;
2053 nsGenericHTMLElement *parent = mCurrentContext->mStack[parentIndex].mContent;
2054 PRInt32 numFlushed = mCurrentContext->mStack[parentIndex].mNumFlushed;
2055 PRInt32 childCount = parent->GetChildCount();
2056 NS_ASSERTION(numFlushed < childCount, "Already notified on the frameset?");
2058 PRInt32 insertionPoint =
2059 mCurrentContext->mStack[parentIndex].mInsertionPoint;
2061 // XXX: I have yet to see a case where numFlushed is non-zero and
2062 // insertionPoint is not -1, but this code will try to handle
2063 // those cases too.
2065 PRUint32 oldUpdates = mUpdatesInNotification;
2066 mUpdatesInNotification = 0;
2067 if (insertionPoint != -1) {
2068 NotifyInsert(parent, mFrameset, insertionPoint - 1);
2069 } else {
2070 NotifyAppend(parent, numFlushed);
2072 mCurrentContext->mStack[parentIndex].mNumFlushed = childCount;
2073 if (mUpdatesInNotification > 1) {
2074 UpdateChildCounts();
2076 mUpdatesInNotification = oldUpdates;
2079 return rv;
2082 nsresult
2083 HTMLContentSink::CloseFrameset()
2085 SINK_TRACE_NODE(SINK_TRACE_CALLS,
2086 "HTMLContentSink::CloseFrameset",
2087 eHTMLTag_frameset,
2088 mCurrentContext->mStackPos - 1,
2089 this);
2091 SinkContext* sc = mCurrentContext;
2092 nsGenericHTMLElement* fs = sc->mStack[sc->mStackPos - 1].mContent;
2093 PRBool done = fs == mFrameset;
2095 nsresult rv;
2096 if (done) {
2097 PRBool didFlush;
2098 rv = sc->FlushTextAndRelease(&didFlush);
2099 if (NS_FAILED(rv)) {
2100 return rv;
2103 // Flush out anything that's left
2104 SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_REFLOW,
2105 ("HTMLContentSink::CloseFrameset: layout final content"));
2107 sc->FlushTags();
2110 rv = sc->CloseContainer(eHTMLTag_frameset, PR_FALSE);
2112 if (done && mFramesEnabled) {
2113 StartLayout(PR_FALSE);
2116 return rv;
2119 NS_IMETHODIMP
2120 HTMLContentSink::IsEnabled(PRInt32 aTag, PRBool* aReturn)
2122 nsHTMLTag theHTMLTag = nsHTMLTag(aTag);
2124 if (theHTMLTag == eHTMLTag_script) {
2125 *aReturn = mScriptEnabled;
2126 } else if (theHTMLTag == eHTMLTag_frameset) {
2127 *aReturn = mFramesEnabled;
2128 } else {
2129 *aReturn = PR_FALSE;
2132 return NS_OK;
2135 NS_IMETHODIMP
2136 HTMLContentSink::OpenContainer(const nsIParserNode& aNode)
2138 nsresult rv = NS_OK;
2140 switch (aNode.GetNodeType()) {
2141 case eHTMLTag_frameset:
2142 rv = OpenFrameset(aNode);
2143 break;
2144 case eHTMLTag_head:
2145 rv = OpenHeadContext();
2146 if (NS_SUCCEEDED(rv)) {
2147 rv = AddAttributes(aNode, mHead, PR_TRUE, mHaveSeenHead);
2148 mHaveSeenHead = PR_TRUE;
2150 break;
2151 case eHTMLTag_body:
2152 rv = OpenBody(aNode);
2153 break;
2154 case eHTMLTag_html:
2155 if (mRoot) {
2156 // If we've already hit this code once, need to check for
2157 // already-present attributes on the root.
2158 AddAttributes(aNode, mRoot, PR_TRUE, mNotifiedRootInsertion);
2159 if (!mNotifiedRootInsertion) {
2160 NotifyRootInsertion();
2162 ProcessOfflineManifest(mRoot);
2164 break;
2165 case eHTMLTag_form:
2166 rv = OpenForm(aNode);
2167 break;
2168 default:
2169 rv = mCurrentContext->OpenContainer(aNode);
2170 break;
2173 return rv;
2176 NS_IMETHODIMP
2177 HTMLContentSink::CloseContainer(const eHTMLTags aTag)
2179 nsresult rv = NS_OK;
2181 switch (aTag) {
2182 case eHTMLTag_frameset:
2183 rv = CloseFrameset();
2184 break;
2185 case eHTMLTag_head:
2186 CloseHeadContext();
2187 break;
2188 case eHTMLTag_body:
2189 rv = CloseBody();
2190 break;
2191 case eHTMLTag_html:
2192 rv = CloseHTML();
2193 break;
2194 case eHTMLTag_form:
2195 rv = CloseForm();
2196 break;
2197 default:
2198 rv = mCurrentContext->CloseContainer(aTag, PR_FALSE);
2199 break;
2202 return rv;
2205 NS_IMETHODIMP
2206 HTMLContentSink::CloseMalformedContainer(const eHTMLTags aTag)
2208 return mCurrentContext->CloseContainer(aTag, PR_TRUE);
2211 NS_IMETHODIMP
2212 HTMLContentSink::AddLeaf(const nsIParserNode& aNode)
2214 nsresult rv;
2216 nsHTMLTag nodeType = nsHTMLTag(aNode.GetNodeType());
2217 switch (nodeType) {
2218 case eHTMLTag_link:
2219 mCurrentContext->FlushTextAndRelease();
2220 rv = ProcessLINKTag(aNode);
2222 break;
2223 default:
2224 rv = mCurrentContext->AddLeaf(aNode);
2226 break;
2229 return rv;
2233 * This gets called by the parsing system when we find a comment
2234 * @update gess11/9/98
2235 * @param aNode contains a comment token
2236 * @return error code
2238 nsresult
2239 HTMLContentSink::AddComment(const nsIParserNode& aNode)
2241 nsresult rv = mCurrentContext->AddComment(aNode);
2243 return rv;
2247 * This gets called by the parsing system when we find a PI
2248 * @update gess11/9/98
2249 * @param aNode contains a comment token
2250 * @return error code
2252 nsresult
2253 HTMLContentSink::AddProcessingInstruction(const nsIParserNode& aNode)
2255 nsresult result = NS_OK;
2256 // Implementation of AddProcessingInstruction() should start here
2258 return result;
2262 * This gets called by the parser when it encounters
2263 * a DOCTYPE declaration in the HTML document.
2266 NS_IMETHODIMP
2267 HTMLContentSink::AddDocTypeDecl(const nsIParserNode& aNode)
2269 nsAutoString docTypeStr(aNode.GetText());
2270 nsresult rv = NS_OK;
2272 PRInt32 publicStart = docTypeStr.Find("PUBLIC", PR_TRUE);
2273 PRInt32 systemStart = docTypeStr.Find("SYSTEM", PR_TRUE);
2274 nsAutoString name, publicId, systemId;
2276 if (publicStart >= 0 || systemStart >= 0) {
2278 * If we find the keyword 'PUBLIC' after the keyword 'SYSTEM' we assume
2279 * that we got a system id that happens to contain the string "PUBLIC"
2280 * and we ignore that as the start of the public id.
2282 if (systemStart >= 0 && (publicStart > systemStart)) {
2283 publicStart = -1;
2287 * We found 'PUBLIC' or 'SYSTEM' in the doctype, put everything before
2288 * the first one of those in name.
2290 docTypeStr.Mid(name, 0, publicStart >= 0 ? publicStart : systemStart);
2292 if (publicStart >= 0) {
2293 // We did find 'PUBLIC'
2294 docTypeStr.Mid(publicId, publicStart + 6,
2295 docTypeStr.Length() - publicStart);
2296 publicId.Trim(" \t\n\r");
2298 // Strip quotes
2299 PRUnichar ch = publicId.IsEmpty() ? '\0' : publicId.First();
2301 PRBool hasQuote = PR_FALSE;
2302 if (ch == '"' || ch == '\'') {
2303 publicId.Cut(0, 1);
2305 PRInt32 end = publicId.FindChar(ch);
2307 if (end < 0) {
2309 * We didn't find an end quote, so just make sure we cut off
2310 * the '>' on the end of the doctype declaration
2313 end = publicId.FindChar('>');
2314 } else {
2315 hasQuote = PR_TRUE;
2319 * If we didn't find a closing quote or a '>' we leave publicId as
2320 * it is.
2322 if (end >= 0) {
2323 publicId.Truncate(end);
2325 } else {
2326 // No quotes, ignore the public id
2327 publicId.Truncate();
2331 * Make sure the 'SYSTEM' word we found is not inside the pubilc id
2333 PRInt32 pos = docTypeStr.Find(publicId);
2335 if (systemStart > 0) {
2336 if (systemStart < pos + (PRInt32)publicId.Length()) {
2337 systemStart = docTypeStr.Find("SYSTEM", PR_TRUE,
2338 pos + publicId.Length());
2343 * If we didn't find 'SYSTEM' we treat everything after the public id
2344 * as the system id.
2346 if (systemStart < 0) {
2347 // 1 is the end quote
2348 systemStart = pos + publicId.Length() + (hasQuote ? 1 : 0);
2352 if (systemStart >= 0) {
2353 // We either found 'SYSTEM' or we have data after the public id
2354 docTypeStr.Mid(systemId, systemStart,
2355 docTypeStr.Length() - systemStart);
2357 // Strip off 'SYSTEM' if we have it.
2358 if (StringBeginsWith(systemId, NS_LITERAL_STRING("SYSTEM")))
2359 systemId.Cut(0, 6);
2361 systemId.Trim(" \t\n\r");
2363 // Strip quotes
2364 PRUnichar ch = systemId.IsEmpty() ? '\0' : systemId.First();
2366 if (ch == '"' || ch == '\'') {
2367 systemId.Cut(0, 1);
2369 PRInt32 end = systemId.FindChar(ch);
2371 if (end < 0) {
2372 // We didn't find an end quote, then we just make sure we
2373 // cut of the '>' on the end of the doctype declaration
2375 end = systemId.FindChar('>');
2378 // If we found an closing quote nor a '>' we truncate systemId
2379 // at that length.
2380 if (end >= 0) {
2381 systemId.Truncate(end);
2383 } else {
2384 systemId.Truncate();
2387 } else {
2388 name.Assign(docTypeStr);
2391 // Cut out "<!DOCTYPE" or "DOCTYPE" from the name.
2392 if (StringBeginsWith(name, NS_LITERAL_STRING("<!DOCTYPE"), nsCaseInsensitiveStringComparator())) {
2393 name.Cut(0, 9);
2394 } else if (StringBeginsWith(name, NS_LITERAL_STRING("DOCTYPE"), nsCaseInsensitiveStringComparator())) {
2395 name.Cut(0, 7);
2398 name.Trim(" \t\n\r");
2400 // Check if name contains whitespace chars. If it does and the first
2401 // char is not a quote, we set the name to contain the characters
2402 // before the whitespace
2403 PRInt32 nameEnd = 0;
2405 if (name.IsEmpty() || (name.First() != '"' && name.First() != '\'')) {
2406 nameEnd = name.FindCharInSet(" \n\r\t");
2409 // If we didn't find a public id we grab everything after the name
2410 // and use that as public id.
2411 if (publicStart < 0) {
2412 name.Mid(publicId, nameEnd, name.Length() - nameEnd);
2413 publicId.Trim(" \t\n\r");
2415 PRUnichar ch = publicId.IsEmpty() ? '\0' : publicId.First();
2417 if (ch == '"' || ch == '\'') {
2418 publicId.Cut(0, 1);
2420 PRInt32 publicEnd = publicId.FindChar(ch);
2422 if (publicEnd < 0) {
2423 publicEnd = publicId.FindChar('>');
2426 if (publicEnd < 0) {
2427 publicEnd = publicId.Length();
2430 publicId.Truncate(publicEnd);
2431 } else {
2432 // No quotes, no public id
2433 publicId.Truncate();
2437 if (nameEnd >= 0) {
2438 name.Truncate(nameEnd);
2439 } else {
2440 nameEnd = name.FindChar('>');
2442 if (nameEnd >= 0) {
2443 name.Truncate(nameEnd);
2447 if (!publicId.IsEmpty() || !systemId.IsEmpty() || !name.IsEmpty()) {
2448 nsCOMPtr<nsIDOMDocumentType> oldDocType;
2449 nsCOMPtr<nsIDOMDocumentType> docType;
2451 nsCOMPtr<nsIDOMDocument> doc(do_QueryInterface(mHTMLDocument));
2452 doc->GetDoctype(getter_AddRefs(oldDocType));
2454 // Assign "HTML" if we don't have anything, and normalize
2455 // the name if it is something like "hTmL", per HTML5.
2456 if (name.IsEmpty() || name.LowerCaseEqualsLiteral("html")) {
2457 name.AssignLiteral("HTML");
2460 nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(name);
2461 if (!nameAtom) {
2462 return NS_ERROR_OUT_OF_MEMORY;
2465 // Indicate that there is no internal subset (not just an empty one)
2466 nsAutoString voidString;
2467 voidString.SetIsVoid(PR_TRUE);
2468 rv = NS_NewDOMDocumentType(getter_AddRefs(docType),
2469 mDocument->NodeInfoManager(), nsnull,
2470 nameAtom, nsnull, nsnull, publicId, systemId,
2471 voidString);
2472 NS_ENSURE_SUCCESS(rv, rv);
2474 if (oldDocType) {
2475 // If we already have a doctype we replace the old one.
2476 nsCOMPtr<nsIDOMNode> tmpNode;
2477 rv = doc->ReplaceChild(oldDocType, docType, getter_AddRefs(tmpNode));
2478 } else {
2479 // If we don't already have one we insert it as the first child,
2480 // this might not be 100% correct but since this is called from
2481 // the content sink we assume that this is what we want.
2482 nsCOMPtr<nsIContent> content = do_QueryInterface(docType);
2483 NS_ASSERTION(content, "Doctype isn't content?");
2485 mDocument->InsertChildAt(content, 0, PR_TRUE);
2489 return rv;
2492 NS_IMETHODIMP
2493 HTMLContentSink::DidProcessTokens(void)
2495 return NS_OK;
2498 NS_IMETHODIMP
2499 HTMLContentSink::WillProcessAToken(void)
2501 return NS_OK;
2504 NS_IMETHODIMP
2505 HTMLContentSink::DidProcessAToken(void)
2507 return DidProcessATokenImpl();
2510 NS_IMETHODIMP
2511 HTMLContentSink::WillInterrupt()
2513 return WillInterruptImpl();
2516 NS_IMETHODIMP
2517 HTMLContentSink::WillResume()
2519 return WillResumeImpl();
2522 NS_IMETHODIMP
2523 HTMLContentSink::NotifyTagObservers(nsIParserNode* aNode)
2525 // Bug 125317
2526 // Inform observers that we're handling a document.write().
2527 // This information is necessary for the charset observer, atleast,
2528 // to make a decision whether a new charset loading is required or not.
2530 if (!mObservers) {
2531 return NS_OK;
2534 PRUint32 flag = 0;
2536 if (mHTMLDocument && mHTMLDocument->IsWriting()) {
2537 flag = nsIElementObserver::IS_DOCUMENT_WRITE;
2540 return mObservers->Notify(aNode, mParser, mDocShell, flag);
2543 void
2544 HTMLContentSink::StartLayout(PRBool aIgnorePendingSheets)
2546 if (mLayoutStarted) {
2547 return;
2550 mHTMLDocument->SetIsFrameset(mFrameset != nsnull);
2552 nsContentSink::StartLayout(aIgnorePendingSheets);
2555 nsresult
2556 HTMLContentSink::OpenHeadContext()
2558 if (mCurrentContext && mCurrentContext->IsCurrentContainer(eHTMLTag_head))
2559 return NS_OK;
2561 // Flush everything in the current context so that we don't have
2562 // to worry about insertions resulting in inconsistent frame creation.
2564 // Try to do this only if needed (costly), i.e., only if we are sure
2565 // we are changing contexts from some other context to the head.
2567 // PERF: This call causes approximately a 2% slowdown in page load time
2568 // according to jrgm's page load tests, but seems to be a necessary evil
2569 if (mCurrentContext && (mCurrentContext != mHeadContext)) {
2570 mCurrentContext->FlushTags();
2573 if (!mHeadContext) {
2574 mHeadContext = new SinkContext(this);
2575 NS_ENSURE_TRUE(mHeadContext, NS_ERROR_OUT_OF_MEMORY);
2577 nsresult rv = mHeadContext->Begin(eHTMLTag_head, mHead, 0, -1);
2578 NS_ENSURE_SUCCESS(rv, rv);
2581 mContextStack.AppendElement(mCurrentContext);
2582 mCurrentContext = mHeadContext;
2584 return NS_OK;
2587 void
2588 HTMLContentSink::CloseHeadContext()
2590 if (mCurrentContext) {
2591 if (!mCurrentContext->IsCurrentContainer(eHTMLTag_head))
2592 return;
2594 mCurrentContext->FlushTextAndRelease();
2595 mCurrentContext->FlushTags();
2598 if (!mContextStack.IsEmpty())
2600 PRUint32 n = mContextStack.Length() - 1;
2601 mCurrentContext = mContextStack.ElementAt(n);
2602 mContextStack.RemoveElementAt(n);
2606 nsresult
2607 HTMLContentSink::ProcessLINKTag(const nsIParserNode& aNode)
2609 nsresult result = NS_OK;
2611 if (mCurrentContext) {
2612 // Create content object
2613 nsCOMPtr<nsIContent> element;
2614 nsCOMPtr<nsINodeInfo> nodeInfo;
2615 nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::link, nsnull, kNameSpaceID_XHTML);
2617 result = NS_NewHTMLElement(getter_AddRefs(element), nodeInfo, PR_FALSE);
2618 NS_ENSURE_SUCCESS(result, result);
2620 nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(element));
2622 if (ssle) {
2623 // XXX need prefs. check here.
2624 if (!mInsideNoXXXTag) {
2625 ssle->InitStyleLinkElement(PR_FALSE);
2626 ssle->SetEnableUpdates(PR_FALSE);
2627 } else {
2628 ssle->InitStyleLinkElement(PR_TRUE);
2632 // Add in the attributes and add the style content object to the
2633 // head container.
2634 result = AddAttributes(aNode, element);
2635 if (NS_FAILED(result)) {
2636 return result;
2639 mCurrentContext->AddLeaf(element); // <link>s are leaves
2641 if (ssle) {
2642 ssle->SetEnableUpdates(PR_TRUE);
2643 PRBool willNotify;
2644 PRBool isAlternate;
2645 result = ssle->UpdateStyleSheet(this, &willNotify, &isAlternate);
2646 if (NS_SUCCEEDED(result) && willNotify && !isAlternate) {
2647 ++mPendingSheetCount;
2648 mScriptLoader->AddExecuteBlocker();
2651 // look for <link rel="next" href="url">
2652 nsAutoString relVal;
2653 element->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, relVal);
2654 if (!relVal.IsEmpty()) {
2655 // XXX seems overkill to generate this string array
2656 nsAutoTArray<nsString, 4> linkTypes;
2657 nsStyleLinkElement::ParseLinkTypes(relVal, linkTypes);
2658 PRBool hasPrefetch = linkTypes.Contains(NS_LITERAL_STRING("prefetch"));
2659 if (hasPrefetch || linkTypes.Contains(NS_LITERAL_STRING("next"))) {
2660 nsAutoString hrefVal;
2661 element->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal);
2662 if (!hrefVal.IsEmpty()) {
2663 PrefetchHref(hrefVal, element, hasPrefetch);
2666 if (linkTypes.Contains(NS_LITERAL_STRING("dns-prefetch"))) {
2667 nsAutoString hrefVal;
2668 element->GetAttr(kNameSpaceID_None, nsGkAtoms::href, hrefVal);
2669 if (!hrefVal.IsEmpty()) {
2670 PrefetchDNS(hrefVal);
2677 return result;
2680 #ifdef DEBUG
2681 void
2682 HTMLContentSink::ForceReflow()
2684 mCurrentContext->FlushTags();
2686 #endif
2688 void
2689 HTMLContentSink::NotifyInsert(nsIContent* aContent,
2690 nsIContent* aChildContent,
2691 PRInt32 aIndexInContainer)
2693 if (aContent && aContent->GetCurrentDoc() != mDocument) {
2694 // aContent is not actually in our document anymore.... Just bail out of
2695 // here; notifying on our document for this insert would be wrong.
2696 return;
2699 mInNotification++;
2702 // Scope so we call EndUpdate before we decrease mInNotification
2703 MOZ_AUTO_DOC_UPDATE(mDocument, UPDATE_CONTENT_MODEL, !mBeganUpdate);
2704 nsNodeUtils::ContentInserted(NODE_FROM(aContent, mDocument),
2705 aChildContent, aIndexInContainer);
2706 mLastNotificationTime = PR_Now();
2709 mInNotification--;
2712 void
2713 HTMLContentSink::NotifyRootInsertion()
2715 NS_PRECONDITION(!mNotifiedRootInsertion, "Double-notifying on root?");
2716 NS_ASSERTION(!mLayoutStarted,
2717 "How did we start layout without notifying on root?");
2718 // Now make sure to notify that we have now inserted our root. If
2719 // there has been no initial reflow yet it'll be a no-op, but if
2720 // there has been one we need this to get its frames constructed.
2721 // Note that if mNotifiedRootInsertion is true we don't notify here,
2722 // since that just means there are multiple <html> tags in the
2723 // document; in those cases we just want to put all the attrs on one
2724 // tag.
2725 mNotifiedRootInsertion = PR_TRUE;
2726 PRInt32 index = mDocument->IndexOf(mRoot);
2727 NS_ASSERTION(index != -1, "mRoot not child of document?");
2728 NotifyInsert(nsnull, mRoot, index);
2730 // Now update the notification information in all our
2731 // contexts, since we just inserted the root and notified on
2732 // our whole tree
2733 UpdateChildCounts();
2736 PRBool
2737 HTMLContentSink::IsMonolithicContainer(nsHTMLTag aTag)
2739 if (aTag == eHTMLTag_tr ||
2740 aTag == eHTMLTag_select ||
2741 aTag == eHTMLTag_applet ||
2742 aTag == eHTMLTag_object) {
2743 return PR_TRUE;
2746 return PR_FALSE;
2749 void
2750 HTMLContentSink::UpdateChildCounts()
2752 PRUint32 numContexts = mContextStack.Length();
2753 for (PRUint32 i = 0; i < numContexts; i++) {
2754 SinkContext* sc = mContextStack.ElementAt(i);
2756 sc->UpdateChildCounts();
2759 mCurrentContext->UpdateChildCounts();
2762 void
2763 HTMLContentSink::PreEvaluateScript()
2765 // Eagerly append all pending elements (including the current body child)
2766 // to the body (so that they can be seen by scripts) and force reflow.
2767 SINK_TRACE(gSinkLogModuleInfo, SINK_TRACE_CALLS,
2768 ("HTMLContentSink::PreEvaluateScript: flushing tags before "
2769 "evaluating script"));
2771 // XXX Should this call FlushTags()?
2772 mCurrentContext->FlushText();
2775 void
2776 HTMLContentSink::PostEvaluateScript(nsIScriptElement *aElement)
2778 mHTMLDocument->ScriptExecuted(aElement);
2781 nsresult
2782 HTMLContentSink::ProcessSCRIPTEndTag(nsGenericHTMLElement *content,
2783 PRBool aMalformed)
2785 // Flush all tags up front so that we are in as stable state as possible
2786 // when calling DoneAddingChildren. This may not be strictly needed since
2787 // any ScriptAvailable calls will cause us to flush anyway. But it gives a
2788 // warm fuzzy feeling to be in a stable state before even attempting to
2789 // run scripts.
2790 // It would however be needed if we properly called BeginUpdate and
2791 // EndUpdate while we were inserting stuff into the DOM.
2793 // XXX Should this call FlushTags()?
2794 mCurrentContext->FlushText();
2796 nsCOMPtr<nsIScriptElement> sele = do_QueryInterface(content);
2797 NS_ASSERTION(sele, "Not really closing a script tag?");
2799 if (aMalformed) {
2800 // Make sure to serialize this script correctly, for nice round tripping.
2801 sele->SetIsMalformed();
2803 if (mFrameset) {
2804 sele->PreventExecution();
2807 // Notify our document that we're loading this script.
2808 mHTMLDocument->ScriptLoading(sele);
2810 // Now tell the script that it's ready to go. This may execute the script
2811 // or return NS_ERROR_HTMLPARSER_BLOCK. Or neither if the script doesn't
2812 // need executing.
2813 nsresult rv = content->DoneAddingChildren(PR_TRUE);
2815 // If the act of insertion evaluated the script, we're fine.
2816 // Else, block the parser till the script has loaded.
2817 if (rv == NS_ERROR_HTMLPARSER_BLOCK) {
2818 // If this append fails we'll never unblock the parser, but the UI will
2819 // still remain responsive. There are other ways to deal with this, but
2820 // the end result is always that the page gets botched, so there is no
2821 // real point in making it more complicated.
2822 mScriptElements.AppendObject(sele);
2824 else {
2825 // This may have already happened if the script executed, but in case
2826 // it didn't then remove the element so that it doesn't get stuck forever.
2827 mHTMLDocument->ScriptExecuted(sele);
2830 // If the parser got blocked, make sure to return the appropriate rv.
2831 // I'm not sure if this is actually needed or not.
2832 if (mParser && !mParser->IsParserEnabled()) {
2833 rv = NS_ERROR_HTMLPARSER_BLOCK;
2836 return rv;
2839 // 3 ways to load a style sheet: inline, style src=, link tag
2840 // XXX What does nav do if we have SRC= and some style data inline?
2842 nsresult
2843 HTMLContentSink::ProcessSTYLEEndTag(nsGenericHTMLElement* content)
2845 nsCOMPtr<nsIStyleSheetLinkingElement> ssle = do_QueryInterface(content);
2847 NS_ASSERTION(ssle,
2848 "html:style doesn't implement nsIStyleSheetLinkingElement");
2850 nsresult rv = NS_OK;
2852 if (ssle) {
2853 // Note: if we are inside a noXXX tag, then we init'ed this style element
2854 // with mDontLoadStyle = PR_TRUE, so these two calls will have no effect.
2855 ssle->SetEnableUpdates(PR_TRUE);
2856 PRBool willNotify;
2857 PRBool isAlternate;
2858 rv = ssle->UpdateStyleSheet(this, &willNotify, &isAlternate);
2859 if (NS_SUCCEEDED(rv) && willNotify && !isAlternate) {
2860 ++mPendingSheetCount;
2861 mScriptLoader->AddExecuteBlocker();
2865 return rv;
2868 void
2869 HTMLContentSink::FlushPendingNotifications(mozFlushType aType)
2871 // Only flush tags if we're not doing the notification ourselves
2872 // (since we aren't reentrant)
2873 if (!mInNotification) {
2874 // Only flush if we're still a document observer (so that our child counts
2875 // should be correct).
2876 if (mIsDocumentObserver) {
2877 if (aType >= Flush_ContentAndNotify) {
2878 FlushTags();
2880 else if (mCurrentContext) {
2881 mCurrentContext->FlushText();
2884 if (aType >= Flush_InterruptibleLayout) {
2885 // Make sure that layout has started so that the reflow flush
2886 // will actually happen.
2887 StartLayout(PR_TRUE);
2892 nsresult
2893 HTMLContentSink::FlushTags()
2895 if (!mNotifiedRootInsertion) {
2896 NotifyRootInsertion();
2897 return NS_OK;
2900 return mCurrentContext ? mCurrentContext->FlushTags() : NS_OK;
2903 NS_IMETHODIMP
2904 HTMLContentSink::SetDocumentCharset(nsACString& aCharset)
2906 if (mDocShell) {
2907 // the following logic to get muCV is copied from
2908 // nsHTMLDocument::StartDocumentLoad
2909 // We need to call muCV->SetPrevDocCharacterSet here in case
2910 // the charset is detected by parser DetectMetaTag
2911 nsCOMPtr<nsIMarkupDocumentViewer> muCV;
2912 nsCOMPtr<nsIContentViewer> cv;
2913 mDocShell->GetContentViewer(getter_AddRefs(cv));
2914 if (cv) {
2915 muCV = do_QueryInterface(cv);
2916 } else {
2917 // in this block of code, if we get an error result, we return
2918 // it but if we get a null pointer, that's perfectly legal for
2919 // parent and parentContentViewer
2921 nsCOMPtr<nsIDocShellTreeItem> docShellAsItem =
2922 do_QueryInterface(mDocShell);
2923 NS_ENSURE_TRUE(docShellAsItem, NS_ERROR_FAILURE);
2925 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
2926 docShellAsItem->GetSameTypeParent(getter_AddRefs(parentAsItem));
2928 nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
2929 if (parent) {
2930 nsCOMPtr<nsIContentViewer> parentContentViewer;
2931 nsresult rv =
2932 parent->GetContentViewer(getter_AddRefs(parentContentViewer));
2933 if (NS_SUCCEEDED(rv) && parentContentViewer) {
2934 muCV = do_QueryInterface(parentContentViewer);
2939 if (muCV) {
2940 muCV->SetPrevDocCharacterSet(aCharset);
2944 if (mDocument) {
2945 mDocument->SetDocumentCharacterSet(aCharset);
2948 return NS_OK;
2951 nsISupports *
2952 HTMLContentSink::GetTarget()
2954 return mDocument;
2957 PRBool
2958 HTMLContentSink::IsScriptExecuting()
2960 return IsScriptExecutingImpl();
2963 #ifdef DEBUG
2965 * This will dump content model into the output file.
2967 * @update harishd 05/25/00
2968 * @param
2969 * @return NS_OK all went well, error on failure
2972 NS_IMETHODIMP
2973 HTMLContentSink::DumpContentModel()
2975 FILE* out = ::fopen("rtest_html.txt", "a");
2976 if (out) {
2977 if (mDocument) {
2978 Element* root = mDocument->GetRootElement();
2979 if (root) {
2980 if (mDocumentURI) {
2981 nsCAutoString buf;
2982 mDocumentURI->GetSpec(buf);
2983 fputs(buf.get(), out);
2986 fputs(";", out);
2987 root->DumpContent(out, 0, PR_FALSE);
2988 fputs(";\n", out);
2992 fclose(out);
2995 return NS_OK;
2997 #endif